关于 plugin-heavy-dep
面向轻量级 DSH Web 插件集成大型依赖的开发技能,涵盖延迟加载、安全提供与渲染、失败回退及发布检查。
主要功能
- 01延迟加载大型依赖
- 02打包为单个 ESM 分块
- 03通过路径校验提供插件文件
- 04清理生成的 SVG
- 05处理模态框缩放与平移
- 06测试回退和路由安全
适用场景
- 01添加按需加载的图表渲染器
- 02避免大型库进入初始包
- 03通过宿主路由提供插件分块
- 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。
- 针对每个陷阱编写回归测试:回退路径(代码块导入失败 → 原始渲染)、清理器(移除恶意通道),以及——对于宿主路由——包含性测试,确保大小写不同但路径仍在允许范围内时会提供服务,而不是拒绝。