Skip to content

Vite import.meta.glob 实现浏览器工具自动发现:DevToolbox 的插件化架构 ​

译注:本文翻译自 dev.to 文章《How DevToolbox auto-discovers browser tools with Vite import.meta.glob》,作者 dan_f8d850ce77481b,原文链接见文末。DevToolbox 是一个基于 TypeScript + Vite 的开发者工具集合,演示地址为 daannnyyyy.github.io/devtoolbox,源码托管于 GitHub。工具输入均在浏览器本地处理,不发送至应用后端。

日常开发中常用的工具——JSON 格式化、Base64 编解码、时间戳转换、URL 解析——单独做一个 HTML 页面并不难。但当你想集成大量工具、且每个工具由不同贡献者维护时,问题就来了:main.ts 很容易变成一个所有人都在争抢修改的巨型 switch 语句。

DevToolbox 的做法是把每个工具视为独立模块。本文梳理其架构:工具契约、基于 Vite import.meta.glob 的注册表、静态前端如何与隐私诉求契合,以及 CI 对新工具的要求。

问题:共享应用与独立工具的矛盾 ​

如果每新增一个工具都要修改中心路由或侧边栏列表,贡献者就会在同一批文件上产生冲突。评审者也无法一眼看出某个 PR 是单纯新增工具,还是悄悄改动了外壳逻辑。

DevToolbox 的目标是:

  1. 每个工具一个文件夹,位于 src/tools/<id>/
  2. 常见场景下无需手动注册
  3. 纯逻辑可脱离 DOM 进行单元测试
  4. 轻量 UI 挂载,只负责该工具自己的面板

工具契约 ​

每个工具在 index.ts 中导出一个 ToolDefinition:

typescript
export interface ToolDefinition {
  id: string;
  name: string;
  description: string;
  category: string;
  keywords?: string[];
  mount: (container: HTMLElement) => void | (() => void);
}

mount 接收一个空容器并构建工具 UI,当用户离开时可返回一个清理函数。元数据(id、name、category 等)驱动侧边栏和 hash 路由(如 #/json-formatter)。

负责数据转换的逻辑放在同级模块中(例如 logic.ts),这样 Vitest 无需挂载 UI 即可测试编码/解码路径。

用 import.meta.glob 自动发现 ​

注册表并不按名称导入工具,而是在构建时向 Vite 请求所有匹配的模块:

typescript
const modules = import.meta.glob('../tools/*/index.ts', { eager: true });

const tools = Object.entries(modules)
  .filter(([path]) => !path.includes('/_template/'))
  .map(([, mod]) => mod.default ?? mod.tool)
  .filter(Boolean)
  .sort((a, b) => a.name.localeCompare(b.name));

(实际文件在接受定义前还会校验 id 和 mount。)

由于 glob 使用了 eager 模式,生产包会预先包含所有被发现的工具。这让运行时保持简单:对于一个小型工具集,无需维护异步 import map。如果目录规模变大,可以切换到惰性 import.meta.glob,而无需改变文件夹结构。

跳过 _template 意味着模板文件夹永远不会作为假工具出现在侧边栏中。

贡献者如何新增工具 ​

预期流程是:

  1. 将 src/tools/_template/ 复制为 src/tools/your-tool-id/
  2. 实现纯函数与测试
  3. 从 index.ts 导出合法的 ToolDefinition
  4. 提交 PR——注册表会自动识别新文件夹

无需编辑任何中心 switch。共享样式和小型 DOM 辅助函数位于 src/core/,让工具在视觉上保持一致,同时互不依赖。

浏览器本地处理 ​

DevToolbox 是一个静态站点(Vite + GitHub Pages),工具本身没有应用 API。当你把 JSON 或 JWT 形式的字符串粘贴进工具时,处理发生在页面 JavaScript 中。

这一设计取舍值得明确说明:

  • 适用场景: 编码、格式化、解析,以及在可用时使用 Web Crypto 进行哈希
  • 并不等于: 浏览器不会通过扩展、截图或网络栈本身泄露数据——只是说明本应用不会把工具输入 POST 到我们的后端(因为根本没有后端)

主题偏好存储在设备的 localStorage 中,这是种子应用中唯一有意为之的客户端持久化。

测试与 CI ​

每个种子工具都为其纯逻辑提供了 Vitest 覆盖(正常路径 + 边界情况)。GitHub Actions 在 main 分支和 PR 上运行:

  • ESLint
  • Prettier --check
  • tsc --noEmit
  • Vitest
  • 生产构建

对于非管理员贡献者,分支保护要求合并前通过 CI 的 build 任务。质量标准记录在仓库中(CONTRIBUTING.md、docs/quality-bar.md):PR 范围明确、逻辑所在之处有测试、不夹带无关改动。

为什么这种形态适合工具集 ​

插件注册表对三个工具来说过于笨重,对完整 IDE 来说又不够用。对于一个希望逐个工具成长的浏览器工具箱,文件夹即工具 + import.meta.glob 恰好落在有用的中间地带:

  • 贡献者拥有一个目录
  • 外壳保持小巧(布局、路由、注册表、样式)
  • 评审者可以把 PR 读作“这个工具的逻辑和 UI 是否符合契约?”

作者表示,欢迎就 eager 发现是否是正确的默认值、还是更倾向于从第一天起就使用惰性加载工具提供反馈。

原文链接 ​

https://dev.to/dan_f8d850ce77481b/how-devtoolbox-auto-discovers-browser-tools-with-vite-importmetaglob-3d6o