mermaid setConfig() API 详解:签名、内部实现与弃用迁移路径
【免费下载链接】mermaidGeneration of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown项目地址: https://gitcode.com/GitHub_Trending/me/mermaid
本篇围绕 mermaid 配置系统中的setConfig()函数展开:它的确切签名与参数语义、基于"消毒(sanitize)+ 深度合并"的内部实现链路、被官方标记为弃用的根本原因,以及在新版 mermaid 中应当如何选用setSiteConfig、updateSiteConfig、addDirective等替代 API 完成同样的配置需求。读完本文,你将能够理解 mermaid 三层配置模型(defaultConfig / siteConfig / currentConfig)的工作机制,并安全地迁移存量代码中对setConfig()的调用。
API 概览
setConfig()是 mermaid 配置模块对外暴露的函数之一,其自动生成 API 文档页位于 docs/config/setup/config/functions/setConfig.md,同模块的完整函数索引见 docs/config/setup/config/README.md。该函数的核心信息如下:
- 函数签名:
setConfig(conf: MermaidConfig): MermaidConfig - 定义位置:packages/mermaid/src/config.ts
- 参数
conf:类型为MermaidConfig,文档中描述为 "The potential currentConfig",即待合并进当前配置的配置对象 - 返回值:
MermaidConfig,即"与消毒后的 conf 合并之后的 currentConfig"(The currentConfig merged with the sanitized conf) - 行为说明:Updates the
currentConfigwith the providedconfafter sanitization —— 在对该对象做安全消毒后,将其更新进currentConfig - 弃用声明(Deprecated):Any changes to the
currentConfigwould be overwritten by the next call toaddDirectiveorreset—— 对currentConfig所做的任何改动,都会被下一次addDirective()或reset()调用所覆盖
该函数通过 packages/mermaid/src/diagram-api/diagramAPI.ts 的export const setConfig = _setConfig;转发导出,属于 mermaid 对外配置 API 的一部分。
setConfig 在 mermaid 配置体系中的位置
理解setConfig必须先理解 mermaid 的配置分层。从 packages/mermaid/src/config.ts 的模块级变量声明可以看出,mermaid 维护了多份配置对象:
// packages/mermaid/src/config.ts export const defaultConfig: MermaidConfig = Object.freeze(config); // 冻结的默认配置 let siteConfig: MermaidConfig = assignWithDepth({}, defaultConfig); // 站点配置(受保护的持久配置) let configFromInitialize: MermaidConfig; // mermaid.initialize() 传入的配置 let directives: MermaidConfig[] = []; // 图表内 %%{init: ...}%% 指令累积 let currentConfig: MermaidConfig = assignWithDepth({}, defaultConfig); // 渲染时真正生效的配置各层含义如下:
| 配置层 | 来源 | 用途 |
|---|---|---|
defaultConfig | 模块内置,Object.freeze冻结 | 一切配置的起点,不可变 |
siteConfig | setSiteConfig/updateSiteConfig | 宿主站点受保护(protected)的重复使用配置,reset()后currentConfig会回到这里 |
configFromInitialize | saveConfigFromInitialize | 用户在mermaid.initialize()中传入的配置 |
directives | addDirective累积 | 每张图内%%{init: ...}%%指令解析出的配置片段 |
currentConfig | 上述各层按优先级深度合并的结果 | 渲染时实际读取的配置 |
setConfig()的特殊之处在于:它不经过siteConfig,而是直接把传入的conf当作"临时指令"作用于currentConfig。这正是它容易被后续操作覆盖的原因——它没有写入任何持久层。
源码实现剖析:一次 setConfig 调用的完整链路
setConfig的实现只有三行核心逻辑:
// packages/mermaid/src/config.ts L106-L110 export const setConfig = (conf: MermaidConfig): MermaidConfig => { updateCurrentConfig(currentConfig, [conf]); return getConfig(); };第一步:进入 updateCurrentConfig 做消毒与深度合并
updateCurrentConfig(siteCfg, _directives)(config.ts)以siteCfg为基座,依次把_directives数组中的每一项合并进来。setConfig传入的[conf]会被当作一个指令处理:
const updateCurrentConfig = (siteCfg: MermaidConfig, _directives: MermaidConfig[]) => { let cfg: MermaidConfig = assignWithDepth({}, siteCfg); // 从基座深拷贝起步 let sumOfDirectives: MermaidConfig = {}; for (const d of _directives) { sanitize(d); // 1. 逐条消毒 sumOfDirectives = assignWithDepth(sumOfDirectives, d); // 2. 指令间深合并 } cfg = assignWithDepth(cfg, sumOfDirectives); // 3. 覆盖到基座之上 // 4. 若指令携带 theme,则重新计算 themeVariables(见下) currentConfig = cfg; checkConfig(currentConfig); // 5. 触发弃用配置项的告警 return currentConfig; };这里的关键工具是assignWithDepth——一个深度合并函数(区别于Object.assign的浅合并)。Object.assign在合并嵌套对象时会整棵替换子树,而assignWithDepth会逐层递归合并,保留未覆盖的兄弟键。这正是为什么"只改一个深层键"也能安全工作的原因:
assignWithDepth与Object.assign的行为差异对照,摘自 mermaid 配置文档的插图 docs/config/img/assignWithDepth.png
这一点在 packages/mermaid/src/config.spec.ts 中有直接的行为验证——"should allow setting partial options" 用例:
configApi.setConfig({ quadrantChart: { chartHeight: 600, }, }); const updatedConfig = configApi.getConfig(); // 未更新的深层选项应保持原值 expect(defaultConfig.quadrantChart!.chartWidth).toEqual( updatedConfig.quadrantChart!.chartWidth );即只传quadrantChart.chartHeight,同级的chartWidth等字段不会被抹掉。
第二步:主题变量的特殊处理
updateCurrentConfig中还有一段针对theme的专门逻辑(config.ts):当指令中声明了theme且该主题存在于内置主题表中时,会取configFromInitialize的themeVariables与指令中的themeVariables深合并后,交给对应主题的getThemeVariables()重算,结果写回cfg.themeVariables。换言之,setConfig传入主题名后,主题变量不是简单赋值,而是经过主题函数推导生成,保证配色体系自洽。
第三步:getConfig 返回的是深拷贝
setConfig末尾调用getConfig()(config.ts):
export const getConfig = (): MermaidConfig => { return assignWithDepth({}, currentConfig); };返回的是currentConfig的一份深度副本而非引用,因此调用方拿到的配置对象可以安全保存、传递而不影响内部状态。源码注释还特意提醒:Avoid calling this function repeatedly —— 应把结果存入变量复用,而不是在渲染循环里反复调用。
sanitize:setConfig 的安全边界
"after sanitization" 是setConfig语义的核心承诺。真正执行消毒的是sanitize()(config.ts),文档页见 docs/config/setup/config/functions/sanitize.md。它对传入对象做三件事(均就地修改options):
- 保护 secure 键:遍历
['secure', ...(siteConfig.secure ?? [])],如果 options 中带有这些键(例如把securityLevel加入secure列表后的键),则记录 debug 日志并delete掉。源码注释特别警告:不要试图在${}中打印options[key]的值,因为恶意脚本可能利用 logger 的字符串化执行任意代码。 - 防原型污染:删除所有以
__开头的键,阻断__proto__之类的原型污染路径。 - 防 XSS:字符串值中若包含
<、>或url(data:,整个键被删除——因为 base64 data URL 里可以藏内联脚本的 SVG;对象值则递归继续消毒。
packages/mermaid/src/config.spec.ts 的 "should respect secure keys when applying directives" 用例验证了这套机制:站点先把fontSize与securityLevel声明为 secure 后,再通过指令尝试改写二者,最终getConfig()取回的仍是站点配置的值,而未被保护的fontFamily则正常生效。
为什么被弃用:被覆盖的语义
setConfig的弃用声明指出:对currentConfig的改动会被下一次addDirective或reset覆盖。从源码结构看,根源在于updateCurrentConfig的计算方式:
reset()(config.ts)会清空directives数组,并以siteConfig为基座重新执行updateCurrentConfig。setConfig写入的内容只存在于currentConfig这一"派生结果"中,一旦重算即被丢弃。addDirective()(config.ts)同样以siteConfig+ 全部directives重新推导currentConfig,setConfig的临时改动不在推导输入里。
config.spec.ts 的两个 reset 用例精确刻画了这一行为:setSiteConfig({fontFamily: 'foo-font', ...})之后调用setConfig({fontFamily: 'baf'}),getConfig()得到'baf';但reset()之后又回到'foo-font'。另一个用例中setConfig({altFontFamily: 'bar-font'})的改动在reset()后变为undefined。也就是说setConfig只能影响"当下到下次重算之间"的配置状态,无法表达持久意图。
迁移指南:用哪个 API 替代 setConfig
根据需求的不同持久性,推荐迁移到以下 API(均在 packages/mermaid/src/config.ts 中定义,且均不处于弃用状态):
| 需求场景 | 推荐 API | 源码位置 | 语义 |
|---|---|---|---|
| 设置站点级、需跨图保持的基线配置 | setSiteConfig(conf) | config.ts#L64-L76 | 以defaultConfig为底重建siteConfig并深合并 conf;带 theme 时重算themeVariables;随后刷新currentConfig |
| 在已有站点配置上增量修改 | updateSiteConfig(conf) | config.ts#L82-L87 | 把 conf 深合并进现有siteConfig,保留其余站点配置 |
| 表达"某一张图生效"的配置 | addDirective(directive) | config.ts#L173-L186 | 对应图内%%{init: ...}%%指令;会先sanitizeDirective,并把孤立的fontFamily提升进themeVariables |
| 回到站点基线 | reset(config?) | config.ts#L194-L198 | 清空 directives,默认以siteConfig重建currentConfig |
以典型迁移为例,原代码:
// 旧写法:临时改动,随时可能被覆盖 mermaid.setConfig({ securityLevel: 'strict', flowchart: { htmlLabels: true } });若这些是站点全局策略,应迁移为:
// 新写法:写入受保护的站点配置层 mermaid.setSiteConfig({ securityLevel: 'strict', flowchart: { htmlLabels: true }, });若是单图级别的差异,则改用图内%%{init: ...}%%指令(经由addDirective进入配置链),它同样经过sanitize消毒并参与主题变量推导。
需要注意的适用前提与限制:
- 所有 API 均受
sanitize的 secure 键约束,securityLevel等敏感键能否被下游配置覆盖,取决于siteConfig.secure的声明; flowchart.htmlLabels自身已弃用(见 config.ts 中的ConfigWarning.FLOWCHART_HTML_LABELS_DEPRECATED与getEffectiveHtmlLabels()),推荐改用根级的htmlLabels;- 本文所述行为以当前仓库 packages/mermaid/src/config.ts 的实现及其单元测试 packages/mermaid/src/config.spec.ts 为准,具体键名与默认值请参考 MermaidConfig 类型定义与 defaultConfig 的当前值。
小结
setConfig()是 mermaid 配置体系早期形态的产物:它通过updateCurrentConfig完成"消毒 → 深度合并 → 主题推导"三步,把临时配置注入currentConfig并返回深拷贝结果。但由于其写入不落在siteConfig持久层,会被addDirective或reset覆盖,官方已将其弃用。对需要站点级持久配置的调用方,应迁移至setSiteConfig/updateSiteConfig;对单图级配置,应使用 init 指令(addDirective)。理解这套三层模型与 sanitize 安全边界后,可以在不破坏既有安全约束的前提下精确控制每张图的渲染参数。
【免费下载链接】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),仅供参考