mcpField
P
plugin-heavy-dep

plugin-heavy-dep

github.com/oh-my-dsh开发305190 次安装1 周前更新
代码仓库

关于 plugin-heavy-dep

面向轻量级 DSH Web 插件集成大型依赖的开发技能,涵盖延迟加载、安全提供与渲染、失败回退及发布检查。

主要功能

  1. 01延迟加载大型依赖
  2. 02打包为单个 ESM 分块
  3. 03通过路径校验提供插件文件
  4. 04清理生成的 SVG
  5. 05处理模态框缩放与平移
  6. 06测试回退和路由安全

适用场景

  1. 01添加按需加载的图表渲染器
  2. 02避免大型库进入初始包
  3. 03通过宿主路由提供插件分块
  4. 04保护不可信标记内容的渲染

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/oh-my-dsh/dsh-plugin-upgrade-skill --skill plugin-heavy-dep

安装后在 Claude Code 中输入/plugin-heavy-dep确认已加载。

附带文件README.md

详细介绍

为轻量级 DSH Web 插件添加大型依赖

轻量级 Web 插件(客户端包体积小,没有构建集群)依然可以提供数 MB 的渲染器——只要确保重型代码仅在需要时加载,并且在无法加载时能够优雅降级。本指南是一份集成检查清单;以下每一项都来自一次真实的 mermaid 集成经验。

1. 决定延迟加载还是内联

如果某个依赖会让客户端包体积膨胀数倍,而只有一项功能需要它(例如围栏渲染器,或按需打开的编辑器),就不要将它内联到客户端包中。应将其拆分为单独的代码块文件,由客户端仅在实际渲染该功能时动态导入。

2. 将代码块打包成一个文件

将依赖打包成单个自包含的 ESM 文件,并禁用代码拆分。通用打包工具如果使用默认设置,会将库内部的动态导入拆分为带内容哈希名称的同级代码块文件——随后浏览器会根据代码块的 URL 将这些文件解析为相对导入,而每个同级文件都必须一并提供、使用完全正确的文件名,并配置正确的 MIME 类型。一个文件、一次导入,就不会有相对路径解析这一类问题。

3. 从限定于自身 lib 的宿主路由提供服务

在 webServer 上注册一个前缀路由,仅提供插件自身 lib 目录中的文件:

  • 从宿主包本身解析 lib 目录(使用 import.meta.url),绝不要使用 process.cwd()。
  • 将扩展名限制在白名单内(代码块使用 .js/.mjs——绝不应提供其他文件)。
  • 路径包含性检查:确认请求路径仍位于 lib 目录内。这里有两条来之不易的经验:
    • 使用 path.relative 比较,而不是 startsWith:只要文件系统的规范化结果与基准字符串不同,前缀比较就会出错。
    • Windows 驱动器号的大小写可能不同:realpathSync 可能返回 e:\…,而你的基准路径是 E:\…;大小写敏感的比较会将实际位于目录内的路径误判为越界,并返回 403。使用 path.relative(并对结果执行 isAbsolute 检查,以处理跨驱动器的情况)更加可靠。
  • 使用 JavaScript MIME 类型(application/javascript)提供文件——MIME 类型错误会导致浏览器拒绝动态导入。

请记住:只有在重启 dsh后,宿主路由才会生效;仅执行强制刷新不会注册新的宿主代码。刚添加路由后遇到 404,几乎总是因为“尚未重启”或“已安装的副本不包含该路由”。

4. 延迟导入、缓存结果,并提供回退

  • 首次渲染该功能时,使用 import() 导入代码块 URL;缓存成功加载的模块,避免重复导入多个围栏。
  • 不要以相同方式缓存失败结果——否则临时故障会持续整个页面生命周期;应允许下次尝试重试,但要进行限流(循环触发失败的导入本身就会造成控制台刷屏)。
  • 导入或渲染出错时,始终渲染一个回退内容(例如原始代码块);绝不能因为图表无法加载而影响阅读。为回退内容添加状态属性,并将失败原因记录到控制台——只知道“已回退”却不知道原因,会让问题无法调试。

5. 不可信标记:在使用 innerHTML 前进行清理

渲染器接收不可信文本(Markdown、模型输出)并生成标记时,在传入 dangerouslySetInnerHTML 前必须将其视为恶意内容:

  • 将渲染器配置为严格模式,并让它输出真正的 SVG 文本,而不是 HTML 标签(HTML 标签位于 <foreignObject> 内——这是 SVG 中唯一承载原始 HTML 的通道)。
  • 禁止库产生全局错误副作用(有些渲染器会在拒绝前将一个巨大的错误 SVG 转储到 document.body)。
  • 然后使用零依赖的白名单流程自行重新清理生成的 SVG:将其解析为 XML(image/svg+xml;解析失败则拒绝整个字符串),仅接受 <svg> 根元素,不区分大小写地移除 foreignObject/script 和外来 HTML 元素,移除 on*/@* 属性,并移除所有 href/xlink:href(静态图表不需要链接;恶意 href 可能会让 GUI 跳转)。这是纵深防御,不是替代防御。

6. 模态框下的交互归属

插件面板上方的全屏缩放/平移模态框在打开时必须拥有所有滚轮事件——包括 Ctrl+滚轮,即使底层窗格已经绑定了 Ctrl+滚轮(用于调整字体大小)。在窗格级处理器中添加模态框存在性检查;否则一次手势会触发两种行为。模态框缩放:以光标为锚点滚动,拖动以平移,支持键盘快捷键,并可通过 Esc/点击遮罩层关闭。

7. 发布上线

  • 将构建后的代码块提交到软件包中(插件会打包 lib/);在 files 中声明它,确保发布的内容范围明确。
  • 在发行说明中说明重启要求(宿主路由)与仅需刷新的情况(客户端)——否则用户会遇到 404。
  • 针对每个陷阱编写回归测试:回退路径(代码块导入失败 → 原始渲染)、清理器(移除恶意通道),以及——对于宿主路由——包含性测试,确保大小写不同但路径仍在允许范围内时会提供服务,而不是拒绝。

评论

支持 Markdown