Hatchet VSCode 扩展开发指南:DAG 可视化插件的架构、解析器与测试实践

发布时间:2026/9/16 18:07:19
Hatchet VSCode 扩展开发指南:DAG 可视化插件的架构、解析器与测试实践 Hatchet VSCode 扩展开发指南DAG 可视化插件的架构、解析器与测试实践【免费下载链接】hatchet An orchestration engine for background tasks, AI agents, and durable workflows项目地址: https://gitcode.com/GitHub_Trending/ha/hatchet本指南基于 Hatchet 仓库中frontend/vscode-extension的官方开发者文档展开系统讲解该扩展从「用户打开工作流文件」到「DAG 面板渲染」的完整链路两阶段解析架构、四种语言的解析规则、hatchet-workflow包装器机制以及手动 / 无依赖 harness / 静态检查三层测试工作流。读完你将掌握如何修改解析器、验证改动并打包发布该可视化扩展也能把「扩展开发 语言解析」的思路迁移到其他编辑器工具链中。快速开始扩展位于仓库 frontend/vscode-extension 目录下使用pnpm作为包管理器。三条核心命令即可进入开发状态cd frontend/vscode-extension pnpm install pnpm run typecheck # 类型检查见下文 Gotcha: transpileOnly —— 构建并不会做类型检查 pnpm run build # webpack 打包到 dist/extension.jsdev 模式然后在 VSCode 中按F5启动扩展开发宿主具体流程见 测试。当前该包还没有自动化测试套件验证依赖「手动 一次性 harness」的组合见 测试。package.jsonfrontend/vscode-extension/package.json还暴露了更多脚本build:prod生产打包、watch监听重建、package生成.vsix、publish发布、test/test:watchvitest已预留。扩展的activationEvents覆盖typescript、python、ruby、go四种语言入口为./dist/extension.js对外仅注册一条命令hatchet.showDag标题 Hatchet: Show Workflow DAG图标$(graph)。架构从文件打开到 DAG 渲染两阶段解析核心设计思想扩展在src/extension.tsfrontend/vscode-extension/src/extension.ts中激活核心思路是把「快速识别」与「完整解析」拆成两趟扫描Pass 1 — 检测快detectLangWorkflowDeclarations(text)只找出工作流的变量名 行号。开销足够小可以在每次按键触发 CodeLens 定位时运行。Pass 2 — 解析全parseLangWorkflows(text)与LspAnalyzer解析构成 DAG 形状的任务tasks和父边parent edges。两趟解析之间还有一个单文件 fallbackcomputeFallbackWorkflow(...)只跑完整解析器从ParsedWorkflow列表中按varName匹配出与 CodeLens 声明对应的那个工作流供 LSP 不可用时兜底渲染。官方文档给出了从「打开文件」到「DAG 出现」的完整数据流┌─────────────────────────────────────────────┐ file opened/edited │ │ │ │ WorkflowAnnotationCache (TS only) │ ▼ │ scans workspace *.ts for hatchet-workflow │ CodeLensProvider ◄──┤ factory functions, keeps them cached │ (provideCodeLenses)│ │ │ └─────────────────────────────────────────────┘ │ detectWorkflowDeclarations(text, languageId, …) ← fast pass 1 scan │ → per-language detector returns WorkflowDeclaration[] ▼ CodeLens Show Hatchet DAG — name (command: hatchet.showDag) │ user clicks ▼ command hatchet.showDag (extension.ts) │ computeFallbackWorkflow(...) → single-file ParsedWorkflow ▼ DagPanel.createOrShow(...) │ LspAnalyzer.analyzeWorkflow(...) ← pass 2: resolve tasks, │ uses the language server to find cross-file task references, │ falls back to the single-file parse if LSP is unavailable ▼ webview (dag-visualizer) draws nodes edges扩展激活与事件监听源码视角activate()src/extension.ts做了四件事创建WorkflowAnnotationCache与WorkflowTypeAnalyzer并把缓存变化事件订阅到 CodeLens 的refresh()扫描完成后缓存会触发onDidChange自动刷新 CodeLens为SUPPORTED_LANGUAGES [typescript, typescriptreact, python, ruby, go]逐一注册同一个 CodeLens provider注册hatchet.showDag命令无参数调用时会从当前活动编辑器重新检测声明并计算 fallback找不到声明则弹出提示Hatchet: No workflow declarations found in this file.监听onDidChangeTextDocument与onDidChangeActiveTextEditor在受支持语言中刷新 CodeLens并调用DagPanel.scheduleUpdate(decl, fallback, doc.uri)触发面板的防抖更新。deactivate()无需手动清理——VSCode 会自动 dispose 所有注册的订阅。文件地图路径职责src/extension.ts激活入口注册各语言的 CodeLens provider、hatchet.showDag命令以及编辑/活动编辑器监听器。src/providers/codelens-provider.tslooksLikeHatchetDocument预过滤再按languageId分发到各语言的 detect/parse 函数是语言分发的唯一「总交换机」。src/parser/workflow-parser.tsTypeScript解析器基于 TypeScript 编译器 API / AST处理泛型、动态名称与hatchet-workflow包装路径。src/parser/python-parser.ts、go-parser.ts、ruby-parser.ts其余语言的正则 / 行级解析器。src/parser/jsdoc-annotations.ts扫描 TS 源码中的hatchet-workflowJSDoc 标签工厂函数。src/analysis/annotation-cache.ts工作区级hatchet-workflow工厂缓存由文件系统 watcher 保持新鲜。src/analysis/lsp-analyzer.tsPass 2向语言服务器查询跨文件任务引用并从每个引用点提取任务。src/panel/dag-panel.ts管理 webview 面板生命周期与防抖更新。src/dag-visualizer/、src/webview/负责布局与绘制图表的 React webview。src/utils/workspace.ts工作区边界辅助如忽略node_modules。面板生命周期与实时更新DagPanelsrc/panel/dag-panel.ts是一个单例createOrShow()已有面板时只做reveal否则用vscode.window.createWebviewPanel创建hatchetDag面板启用脚本、localResourceRoots限定为dist、retainContextWhenHidden: true。编辑触发的是scheduleUpdate()——500ms 防抖后重新运行分析避免每次击键都触发昂贵重算。运行分析时先postMessage({ type: setLoading, workflowName })让面板进入加载态并把面板标题改为Hatchet DAG — name对于localOnly函数作用域内构建的 DAG直接用单文件 fallback 结果跳过 LSP 跨文件步骤文档特意注明此时单文件解析就是正确答案不应显示「语言服务器不可用」的误导性横幅否则调用LspAnalyzer.analyzeWorkflow(...)并把usedFallback标记随setShape消息一并发给 webview。workflowToShape()把「每个任务存parentVarIds」的解析结果反向转换成 webview 需要的childrenStepIds即DagShapeDagNode[]。点击节点会弹出 QuickPickView source跳转时校验任务fileUri必须位于当前工作区内避免路径穿越 / 打开重定向。各语言解析器识别什么四种语言都识别三样东西workflow 声明、该 workflow 变量上的任务声明、以及每个任务的父任务parents。下面分别对照源码中的识别模式展开。Workflow 名称workflow 名称就是 DAG 的标签。TypeScript可以是字符串字面量或动态表达式——对于stub.name这类非字面量直接使用表达式文本作为标签保证 workflow 依然能渲染这正是resolveWorkflowName修复的场景见 src/parser/workflow-parser.ts有字符串字面量用字面量否则nameExpr.getText(sourceFile).trim()兜底再退到变量名。Python / Go / Ruby 目前要求字符串字面量名称。语言Workflow 声明名称TypeScriptconst wf hatchet.workflow...({ name: X })字面量或动态stub.namePythonwf hatchet.workflow(namex)仅字面量Gowf : client.NewWorkflow(x)仅字面量Rubywf hatchet.workflow(name: x)仅字面量任务与父任务语言任务父任务TypeScriptwf.task({ name, parents })或wf.task(name, { parents })parents: [a, b]Pythonwf.task(...)置于def step(...)之上parents[a, b]Gos : wf.NewTask(name, ...)hatchet.WithParents(a, b)Rubys wf.task(:name, ...)parents: [a, b]源码中对应的具体识别规则TypeScriptASTcollectWorkflows采用task-first任务优先判定——任何变量只要接收task/durableTask调用就被视为 workflow不管它是由*.workflow(...)直接构造、经await的工厂/构造器、还是深嵌多层 wrapper。DEFAULT_TASK_METHODS [task, durableTask]可通过注解扩展。任务名支持两种调用形态位置命名wf.task(step1, { parents })与选项对象wf.task({ name, parents })匿名任务自动生成varId如wf_task_1。Python正则workflow 匹配^(\w)\s*\s*\S\.workflow\s*\(\s*name\s*\s*[]...[]任务匹配行首允许缩进的varName.task(装饰器并在其后 10 行内查找def/async def作为displayName与跳转定位行parents[...]只接受裸标识符/^\w$/。Go正则 括号感知workflow 匹配^(\w)\s*:\s*\S\.NewWorkflow\s*\(\s*...任务匹配[varName :|_ ] workflowVar.NewTask(taskName, ...)。varId优先用赋值的 Go 变量名_除外否则回退到任务名字符串WithParents(...)用大括号感知的括号计数提取避免函数字面量func(...) {...}干扰计数。Ruby正则workflow 匹配varName ...workflow(name: ...)任务匹配[CONST ]varName.task(:name, ...)父任务解析parents: [:a, :b]时自动剥离 symbol 前缀:。hatchet-workflow包装器特性仅 TypeScriptTS 工厂函数可以打上hatchet-workflowJSDoc 标签标记为「工作流包装器」这样对它的调用处会被当作 workflow附着在返回值上的任务构成 DAG/** hatchet-workflow */ function createWorkflowBuilder(stub) { return hatchet.workflow({ name: stub.name }); } const ordersDag createWorkflowBuilder({ name: orders-dag }); // ← DAG renders here const start ordersDag.task({ name: start });包装器通过WorkflowAnnotationCachesrc/analysis/annotation-cache.ts跨工作区解析工厂与调用处可以位于不同文件。其机制是初始化时用findFiles(**/*.{ts,tsx}, {**/node_modules/**,**/.git/**})全量扫描再以文件系统 watchercreate/change/delete增量维护内部按(fileKey → functionName)两级存储变更时重建扁平视图。缓存还支持两个可选注解用于适配自定义 wrapper API标签默认值用途hatchet-task-methodtask注册任务的方法名hatchet-task-parentsparents任务选项对象中列出父任务的属性名scanFileForWorkflowAnnotationssrc/parser/jsdoc-annotations.ts支持四种写法/** hatchet-workflow */ function foo(...)、export function foo(...)、const foo (...) {}、const foo function(...) {}并优先做sourceText.includes(hatchet-workflow)快速 bail避免对无注解文件做完整解析。READMEfrontend/vscode-extension/README.md中给出了带自定义方法名/父属性名的完整示例/** * hatchet-workflow * hatchet-task-method task * hatchet-task-parents parents */ export function createWorkflowBuilder(options) { ... }注意Python / Go / Ruby 没有对应的包装器特性。一个可运行的示例位于 frontend/vscode-extension/examples/wrapper-dag/workflow.ts——它展示了真实 Hatchet SDK v1 的用法类型驱动的检测会让「通过Promise/await/ 自定义别名 / 类型推断解析到WorkflowDeclaration/TaskWorkflowDeclaration/BaseWorkflowDeclaration基类型的任何对象」都获得 DAG lens无论包装多少层同文件还演示了wf.durableTask(...)与wf.task(...)混用的 DAGfetch-data→analyze→report以及无包装的直接声明directWorkflow。CodeLens 预过滤与类型推断looksLikeHatchetDocumentsrc/providers/codelens-provider.ts按语言做廉价预过滤TS 看是否包含hatchet-dev/typescript-sdk、.workflow(/.workflow(模式或hatchet-workflow以及缓存中的工厂函数名Python 看hatchet_sdk或.workflow(Ruby 看.workflow(Go 看.NewWorkflow(。命中后才进入正式解析。provideCodeLenses对 TS/TSX 优先走WorkflowTypeAnalyzer的类型推断任何类型解析到 Hatchet workflow 基类型的变量都加 lenslocalOnly: a.kind function推断失败或无结果时回退到静态语法扫描无 tsconfig、未安装 SDK 等场景两层兜底保证了健壮性。测试官方文档强调「如何测试一个改动」是本指南最重要的部分。测试分三层。1. 在扩展开发宿主中手动测试真实场景在 VSCode 中打开frontend/vscode-extension文件夹。按F5→ 选择Run Extension (wrapper-dag examples)。启动配置.vscode/launch.json会先运行build任务然后在第二个 VSCode 窗口中打开examples/wrapper-dag/并加载扩展。打开examples/wrapper-dag/workflow.ts。workflow 变量上方应出现Show Hatchet DAGCodeLens。点击 → DAG 面板打开。编辑某个任务新增一个、修改parents列表观察面板实时更新。修改扩展代码后需要重载重新构建pnpm run build或在终端运行pnpm run watch然后在开发宿主中执行Developer: Reload Window。watch脚本会在保存时持续重建dist/。2. 用一次性 harness 测解析器逻辑快零依赖解析器都是纯函数无需 VSCode 即可直接调用。TS 解析器只 import 了typescript真实依赖并对vscode仅使用类型import type所以在纯 Node 下即可运行。官方推荐的配方cd frontend/vscode-extension # 写一个快速脚本例如 /tmp/t.ts从 ./src/parser/... 导入 node_modules/.bin/tsc /tmp/t.ts --module commonjs --target ES2020 \ --moduleResolution node --esModuleInterop --skipLibCheck \ --outDir ./__scratch node ./__scratch/t.js rm -rf ./__scratch示例t.ts验证动态名称修复import { detectTsWorkflowDeclarations, parseWorkflows } from ./src/parser/workflow-parser; const src const workflow hatchet.workflowTInput, Out({ name: stub.name }); const start workflow.task({ name: start }); const end workflow.task({ name: end, parents: [start] }); ; console.log(detectTsWorkflowDeclarations(src, t.ts)); // → [{ name: stub.name, varName: workflow, ... }] console.log(parseWorkflows(src, t.ts)[0].tasks); // → start, end (end parented to start)这正是动态名称修复的验证方式。务必把输出目录与 scratch 文件放在src/之外避免被 webpack 打进 bundle。仓库中已沉淀的测试可以参考src/parser/tests/workflow-parser.test.ts 与 src/analysis/tests/workflow-type-analyzer.test.ts它们可直接用pnpm testvitest运行。3. 静态检查视作 CIpnpm run typecheck # 必须通过 —— 构建不做类型检查见下方 gotcha pnpm run build # 必须成功并产出 dist/extension.js想要一套真正的测试套件目前还没有。最轻量的路径是vitest解析器不需要 VSCode 运行时pnpm add -D vitest、在package.json增加test: vitest run然后把 harness 用例迁移到src/parser/__tests__/*.test.ts。LspAnalyzer与面板依赖 VSCode API更适合用手动流程或vscode/test-electron覆盖。package.json中其实已预置了test/test:watch脚本可直接上手。构建与打包pnpm run build/build:prod—— webpack 打包开发 / 生产模式。pnpm run watch—— 变更时自动重建开发宿主内联开发时使用。pnpm run package—— 通过vsce产出.vsix可用Extensions: Install from VSIX…安装。pnpm run publish—— 通过vsce发布仅维护者。GotchatranspileOnlywebpack 构建使用ts-loader且开启transpileOnly: true因此类型错误不会让构建失败——它们只会在运行时暴露或者如果坏代码路径没被走到就永远不暴露。所以在信任一次构建前务必先跑pnpm run typecheck。文档记录了一个真实事故一个越界作用域的变量引用编译通过了悄悄破坏了跨文件任务解析直到 typecheck 才把它揪出来。为新的模式或语言添加支持若你想让扩展识别新的任务模式或新语言官方给出的步骤在src/parser/下新增/扩展一个解析器暴露两个函数detectLangWorkflowDeclarations(text)pass 1与parseLangWorkflows(text)pass 2。把languageId接入 src/providers/codelens-provider.ts 中的交换机detectWorkflowDeclarations、parseWorkflowsForDocument以及looksLikeHatchetDocument。跨文件任务解析在 src/analysis/lsp-analyzer.ts 中按languageId新增extractLangTask(...)分支现有实现为四种语言各写了extractTsTask/extractPyTask/extractGoTask/extractRubyTask并把 URI 扩展名映射到languageId因为跨文件引用是从磁盘直接读取的没有打开的文档可查。如果是新语言把它加入SUPPORTED_LANGUAGESsrc/extension.ts与activationEventspackage.json。在examples/下新增示例并用 harness F5 验证。几个值得注意的实现细节LSP 分析结果上限 500 条并过滤到工作区内防生成文件 /node_modules失控提取任务后还会校验parentVarIds——丢弃那些未被收集到的任务引用保证 DAG 边始终有效extractTsTask同时支持位置命名与选项对象两种形态且会读取注解中的自定义taskMethod/taskParentsProp。一张图看懂交互效果安装扩展后每个 Hatchet DAG 定义上方都会出现 CodeLens 按钮点击即在侧栏打开实时更新的 webview 面板两张截图均取自扩展的 assets 目录前者展示hatchet.workflow声明上方的 CodeLens后者展示由first-step → second-step → third/fourth-step构成的 DAG 面板。小结从「打开文件」到「DAG 出现」Hatchet VSCode 扩展用「快检测 全解析 LSP 兜底」的清晰分层把复杂度收敛在providers、parser、analysis、panel四个模块内。对扩展开发者而言transpileOnly陷阱、防抖刷新、工作区边界过滤与「scratch 文件放src/之外」这些细节都是可以复用到其他工具链的工程经验而hatchet-workflow/hatchet-task-method/hatchet-task-parents三枚 JSDoc 注解则为 TS 用户提供了一条零配置适配自定义 wrapper 的优雅路径。【免费下载链接】hatchet An orchestration engine for background tasks, AI agents, and durable workflows项目地址: https://gitcode.com/GitHub_Trending/ha/hatchet创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考