Composio 文档构建期 TypeScript 代码块类型检查(Twoslash)机制全解
【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio
Composio 官方文档(位于docs/目录的 Next.js + Fumadocs 站点)将Twoslash 构建时类型检查作为保障文档与 SDK 同步的核心手段:所有 TypeScript 代码块在bun run build时都会经过真实的 TypeScript 编译器校验,任何类型错误都会直接导致构建失败。本文以 docs/agent-guidance/context/twoslash.md 为骨架,结合 docs/source.config.ts 的实际配置与 .github/workflows/docs-typescript-check.yml 的 CI 流水线,完整讲解该机制的配置、常用写法、注解语法与排错指南,帮助你在撰写 Composio 文档或搭建同类文档工程时,让每一段 TypeScript 示例都保持"可编译、可运行、与 SDK 零漂移"。
核心机制:所有 TS 代码块在构建期被真实编译
Twoslash 原本是 TypeScript 官方用于交互式展示类型推断的工具("在代码上悬停即可看到类型")。Composio 文档工程对它做了一次"去交互化"改造——只保留类型检查能力,关闭悬停 UI,并让它默认作用于全部 TypeScript 代码块:
- 默认开启,无需任何注解:
ts、typescript、tsx语言标记的代码块全部被校验;Python 代码块不参与类型检查(对应原文档中的 Note 说明)。 - 构建期失败即终止:类型错误会直接让
next build失败,从源头阻止"示例代码已经过时"的文档合入主分支。 - 开发环境关闭:
bun dev时 Twoslash 被禁用,以避免堆内存问题;它只运行在bun run build与 CI 中。这一点在 docs/agent-guidance/context/fumadocs.md 的 "Common Gotchas" 中亦有印证:"Twoslash is disabled inbun devdue to heap memory issues"。
这一设计回答了文档维护中最棘手的问题:如何让示例代码跟上 SDK 演进。当@composio/core升级改变了 API 签名时,文档里旧的调用写法会在下次构建时立刻暴露为 TS 错误,而不是等读者复制粘贴后才发现跑不通。
配置实现:source.config.ts 中的 transformerTwoslash
Twoslash 的实际接线发生在文档工程的构建配置 docs/source.config.ts 中,它在全局mdxOptions.rehypeCodeOptions.transformers里注册了transformerTwoslash,且只在生产构建(NODE_ENV === 'production')下生效:
// docs/source.config.ts(节选) transformers: process.env.NODE_ENV === 'production' ? [ transformerTwoslash({ explicitTrigger: false, twoslashOptions: { compilerOptions: { jsx: 4, // JsxEmit.ReactJSX jsxImportSource: 'react', ignoreDeprecations: '6.0', types: ['node'], }, }, typesCache: createFileSystemTypesCache({ dir: '.next/cache/twoslash', }), renderer: { // Empty renderer - type checks but renders nothing nodeStaticInfo: () => ({}), nodeError: () => ({}), nodeQuery: () => ({}), nodeCompletion: () => ({}), }, }), ] : [],几个值得展开的配置点:
explicitTrigger: false:关闭显式触发开关。默认情况下 Shiki 的 twoslash transformer 只处理包含@twoslash注释的代码块;设为false后,所有TypeScript 代码块都会被纳入检查,这正是原文档 "Default on: All TypeScript blocks are validated. No annotation needed" 的底层来源。- 空 renderer:
nodeStaticInfo、nodeError、nodeQuery、nodeCompletion四个渲染回调全部返回空对象,即只做编译校验、不向页面注入悬停 UI 与错误气泡,保持文档代码块外观与普通高亮一致。 - 文件系统类型缓存:通过
createFileSystemTypesCache将类型解析结果缓存到.next/cache/twoslash,避免每次构建对相同导入重复解析,缩短构建时间。 - compilerOptions 细节:
jsx: 4对应JsxEmit.ReactJSX(配合jsxImportSource: 'react',使tsx代码块能正确编译 JSX);ignoreDeprecations: '6.0'用于屏蔽 TS 6 对虚拟环境中默认baseUrl的弃用警告(TS5101);types: ['node']则显式引入 Node 类型,弥补 TS 6 不再自动包含@types/node的行为变化(否则crypto、process、Buffer等全局会报 TS2591)。这些细节充分说明:文档代码块是在 Twoslash 自己的虚拟 TS 环境中编译的,需要单独配置才能获得与根tsconfig.json一致的类型解析能力。
CI 强制:任何 docs/ 改动都必须通过类型检查
Twoslash 的约束力来自 CI。工作流 .github/workflows/docs-typescript-check.yml(名称为 "Docs - Lint and TypeScript Validation")在所有触及docs/**的 PR上运行,步骤依次为:
bun install --frozen-lockfile安装依赖;bun run lint(oxlint);bun run types:check;bun run build—— 注释明确写着 "Build (validates Twoslash TypeScript code blocks)"。
也就是说,一个 PR 只要修改了文档目录,就必须保证其中每个 TS 代码块能通过真实编译,否则无法合并。工作流还缓存了~/.bun/install/cache以加速依赖安装;docs/内的 pipeline 总览见 docs/agent-guidance/context/pipelines.md 中的 "Lint + TypeScript" 一行。
排除项:content/reference 目录不参与检查
并非所有 TS 代码块都需要类型检查。自动生成的 SDK/API 参考文档位于docs/content/reference/,它们由 OpenAPI 与 SDK 构建产物自动生成,内容不依赖手工维护,因此通过集合级mdxOptions被整体排除在 Twoslash 之外:
// docs/source.config.ts(节选) export const reference = defineDocs({ dir: 'content/reference', docs: { schema: docsSchema, postprocess: { includeProcessedMarkdown: true }, mdxOptions: applyMdxPreset({ remarkPlugins: [remarkMdxMermaid], rehypeCodeOptions: { themes: { light: 'github-light', dark: 'github-dark' }, // No twoslash transformer - SDK reference docs skip type checking }, }), }, ... });注意两点工程细节:其一,这里使用applyMdxPreset重新声明rehypeCodeOptions,只配置了themes而未挂载 twoslash transformer,从而覆盖全局配置;其二,代码注释提示applyMdxPreset是"替换而非合并"("replaces, not merges"),所以必须同时带上remarkPlugins: [remarkMdxMermaid],否则合并进参考文档的 Mermaid 图表会失去渲染插件。同理,content/docs、content/examples、content/toolkits、content/kb等集合都走全局配置,TS 代码块照常被检查。
常用模式一:用---cut---隐藏 setup 代码
文档示例往往需要导入与初始化代码,但它们不该占据读者视线。// ---cut---注释把代码块切成两段:上方的代码参与编译、不参与渲染,下方的代码同时参与编译与渲染。原文档给出的标准模板:
```typescript import { Composio } from '@composio/core'; const composio = new Composio({ apiKey: 'key' }); const userId = 'user_123'; // ---cut--- // Only code below this line is shown in docs const tools = await composio.tools.get(userId, { toolkits: ['GITHUB'] }); ```这种写法在真实文档中大量落地。例如 docs/content/docs/auth-configuration/connected-accounts.mdx 中,每个 TypeScript Tab 都在new Composio({ apiKey: 'your_api_key' })之后插入// ---cut---,再展示composio.connectedAccounts.list(...)等真正的教学代码。读者看到的是干净的业务逻辑,而编译器看到的是一段完整可编译的程序。
常用模式二:优先使用 SDK 导出的回调类型
在编写modifySchema、beforeExecute、afterExecute这类修饰器回调时,与其手写内联类型标注(容易与 SDK 实际签名漂移),不如直接导入@composio/core导出的类型:
```typescript import { Composio, TransformToolSchemaModifier } from '@composio/core'; const modifySchema: TransformToolSchemaModifier = ({ toolSlug, toolkitSlug, schema }) => { // TypeScript infers all parameter types! return schema; }; ```@composio/core提供的修饰器回调类型包括:
| 类型 | 适用回调 |
|---|---|
beforeExecuteModifier | beforeExecute回调 |
afterExecuteModifier | afterExecute回调 |
TransformToolSchemaModifier | modifySchema回调 |
这样参数对象(toolSlug、toolkitSlug、schema)的类型全部由 SDK 推断,回调签名一旦变化,构建期即可发现。从 docs/package.json 可见@composio/core(当前版本^0.18.1)连同@composio/openai、@composio/langchain等各框架适配包都被安装在devDependencies中——这正是 Twoslash 能在虚拟环境里解析这些导入的前提。
常用模式三:跳过检查与声明外部变量
跳过类型检查:当示例依赖未安装的第三方包(如尚未发布的模块)时,可在代码块开头加// @noErrors,该块将不做任何类型校验:
```typescript // @noErrors import { SomeExternalThing } from 'not-installed-package'; ```声明外部变量:当代码使用了片段内未定义的变量时,应在---cut---上方用declare声明,让它既参与编译又不出现在输出中:
```typescript import { Composio } from '@composio/core'; declare const composio: Composio; declare const userId: string; // ---cut--- const tools = await composio.tools.get(userId, { toolkits: ['GITHUB'] }); ```这一模式解决的是 TS 错误 2304("Cannot find name"):在隐藏段声明变量,相当于为片段补齐了运行环境,编译通过的同时保持输出整洁。类似地,需要展示特定错误时可用// @errors: 2322声明期望的错误码(构建不会因此失败);需要展示某位置的类型时用// ^?注释。
注解速查表
| 注解 | 作用 |
|---|---|
// ---cut--- | 隐藏上方代码(参与编译但不输出) |
// @noErrors | 跳过该代码块的全部类型检查 |
// @errors: 2322 | 声明该块期望出现指定错误码,构建不失败 |
// ^? | 在该位置展示悬停类型(本仓库 renderer 为空,主要用于校验场景) |
配置要点与依赖清单
要让 Twoslash 正常工作,工程层面有三件事必须做对:
- 注册 transformer:在 docs/source.config.ts 的全局
rehypeCodeOptions.transformers中启用transformerTwoslash(生产环境生效),并为参考文档集合单独覆盖rehypeCodeOptions以排除检查; - 安装 SDK 包为 devDependencies:Twoslash 在虚拟 TS 环境里解析代码块中的导入,因此文档中引用的
@composio/*包必须出现在 docs/package.json 的devDependencies(同时也用到@shikijs/twoslash与@shikijs/vitepress-twoslash的createFileSystemTypesCache); - 本地构建验证:开发阶段 Twoslash 被禁用,推送前必须本地执行
bun run build验证全部代码块,再由 CI 中的docs-typescript-check.yml兜底。
Troubleshooting 速查
| 问题 | 解决方案 |
|---|---|
| 导入失败 | 确认对应包已加入devDependencies |
| 依赖外部包 | 对未在 package.json 中的示例使用// @noErrors |
| 需要 setup 代码 | 用// ---cut---加入可编译但不展示的导入/声明 |
| 错误码 2304(Cannot find name) | 在隐藏段用declare声明变量 |
| 错误码 2322(类型不匹配) | 修正类型,或改用 SDK 导出的类型(如TransformToolSchemaModifier) |
| 回调参数类型 | 优先导入 SDK 类型,避免手写{ foo: string }内联标注 |
最后再次强调原文档的收尾建议:推送前始终在本地运行bun run build——这是你在进入 CI 前验证所有代码块的唯一可靠手段,也是让 Composio 文档保持"示例即真相"的最后一公里。
【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考