vitest-plugin-ai-doc
v1.0.1
Published
A Vitest reporter plugin for generating AI-friendly documentation with semantic labels and examples
Downloads
89
Maintainers
Readme
Vitest Plugin AI Docs
一个 Vitest Reporter 插件,用于生成 AI 友好的代码注释和文档。通过分析测试用例和源码 AST,自动提取语义标签、示例代码,并支持多种输出格式。
功能特性
- 测试用例分析:从测试代码中提取示例和语义信息
- AST 源码分析:解析 JSDoc 注释,补充测试未覆盖的代码
- AI 智能补充:支持接入大模型 API,自动补充缺失的描述、标签和示例
- 多种输出格式:支持 AI-Context.json、.d.ts 注释注入、Markdown 文档
- 状态标签:支持
@important、@outdated、@new、@ignore等标签 - 增量分析:基于文件 hash 的缓存机制,提升性能
- 覆盖率报告:统计测试覆盖和 AST 分析的覆盖情况
- CLI 工具:提供命令行工具,支持交互式配置 AI 分析
安装
npm install vitest-plugin-ai-doc --save-dev快速开始
// vitest.config.js
import { defineConfig } from 'vitest/config'
import DocReporter from 'vitest-plugin-ai-doc'
export default defineConfig({
test: {
reporters: [
new DocReporter({
outputFormat: 'ai-context',
outputDir: './docs',
enableAstAnalysis: true
})
]
}
})运行测试后,将在 ./docs 目录生成 AI-Context.json 文件。
工作流程
触发机制
本插件实现了 Vitest 的 Reporter 接口,配置后 Vitest 运行测试时会自动调用:
import type { Reporter, Vitest, File } from 'vitest'
export class DocReporter implements Reporter {
// 实现生命周期方法
}触发方式:
# 运行测试时自动触发
vitest run
# 或者 watch 模式
vitest
# 或者通过 npm scripts
npm test生命周期详解
┌────────────────────────────────────────────────────────────────────┐
│ Vitest 测试运行流程 │
├────────────────────────────────────────────────────────────────────┤
│ │
│ 1. vitest 命令执行 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ onInit(ctx: Vitest) │ │
│ │ ───────────────────── │ │
│ │ • Vitest 初始化时调用 │ │
│ │ • 我们保存 vitest 上下文 │ │
│ │ • 初始化日志输出 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ onPathsCollected(paths?: string[]) │ │
│ │ ──────────────────────────────── │ │
│ │ • Vitest 扫描完测试文件后调用 │ │
│ │ • paths 是所有测试文件路径 │ │
│ │ • 我们记录文件数量 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ onCollected(files?: File[]) │ │
│ │ ────────────────────────── │ │
│ │ • Vitest 解析完测试文件后调用 │ │
│ │ • files 包含所有测试用例的 AST 信息 │ │
│ │ • 【核心】我们在这里收集测试元数据: │ │
│ │ - 解析 import 语句 │ │
│ │ - 识别测试目标(函数/类/方法) │ │
│ │ - 提取语义标签 │ │
│ │ - 记录代码片段作为示例 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 测试执行中... │ │
│ │ • 每个测试用例运行 │ │
│ │ • 我们不干预测试执行 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ onFinished() │ │
│ │ ────────────── │ │
│ │ • 所有测试完成后调用 │ │
│ │ • 【核心】我们在这里生成文档: │ │
│ │ 1. 聚合测试元数据 │ │
│ │ 2. 生成测试覆盖的注释 │ │
│ │ 3. 运行 AST 分析(可选) │ │
│ │ 4. 合并覆盖率 │ │
│ │ 5. 输出结果文件 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 输出文件生成完成 │
│ - AI-Context.json │
│ - .d.ts 注释 │
│ - API.md │
│ │
└────────────────────────────────────────────────────────────────────┘生命周期阶段总结
| 阶段 | 方法 | 我们做的事情 |
|------|------|-------------|
| 初始化 | onInit | 保存上下文,输出日志 |
| 文件收集 | onPathsCollected | 记录文件数量 |
| 测试解析 | onCollected | 收集测试元数据 |
| 测试完成 | onFinished | 生成文档输出 |
内部处理流程
┌─────────────────────────────────────────────────────────────────┐
│ onFinished 内部流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────┐ │
│ │ 测试用例分析 │ │
│ │ - 聚合测试元数据 │ │
│ │ - 生成注释数据 │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────▼──────────────────┐ │
│ │ AST 源码分析(可选) │ │
│ │ - 扫描源码目录 │ │
│ │ - 解析 JSDoc 注释 │ │
│ │ - 提取未覆盖的导出 │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────▼──────────────────┐ │
│ │ 覆盖率合并 │ │
│ │ - 合并测试 + AST 结果 │ │
│ │ - 计算覆盖率统计 │ │
│ └──────────────┬──────────────────┘ │
│ │ │
│ ┌──────────────▼──────────────────┐ │
│ │ 输出生成 │ │
│ │ - AI-Context.json │ │
│ │ - .d.ts 注释注入 │ │
│ │ - API.md 文档 │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘配置项
interface ReporterOptions {
verbose?: boolean // 是否输出详细日志,默认 false
outputDir?: string // 输出目录,默认 './docs'
dtsPath?: string // .d.ts 文件路径,用于注入注释
semanticKeywords?: Record<string, string[]> // 自定义语义关键词
sourceDir?: string // 源码目录,默认 './src'
cachePath?: string // 缓存文件路径
enableAstAnalysis?: boolean // 启用 AST 分析,默认 true
outputFormat?: OutputFormat // 输出格式,默认 'both'
aiContextFileName?: string // AI-Context 文件名,默认 'AI-Context.json'
enableAI?: boolean // 启用 AI 补充分析,默认 false
aiConfig?: AIConfig // AI 配置
}
interface AIConfig {
enabled: boolean // 是否启用
apiUrl?: string // API 地址
apiKey?: string // API 密钥
modelName?: string // 模型名称
maxTokens?: number // 最大 token 数,默认 500
temperature?: number // 温度参数,默认 0.3
}
type OutputFormat = 'ai-context' | 'dts' | 'both'配置示例
只生成 AI-Context.json
new DocReporter({
outputFormat: 'ai-context',
outputDir: './docs',
aiContextFileName: 'AI-Context.json'
})只注入 .d.ts 注释
new DocReporter({
outputFormat: 'dts',
dtsPath: './dist/index.d.ts'
})生成所有输出
new DocReporter({
outputFormat: 'both',
outputDir: './docs',
dtsPath: './dist/index.d.ts',
sourceDir: './src',
enableAstAnalysis: true,
verbose: true
})自定义语义关键词
new DocReporter({
semanticKeywords: {
'请求': ['request', 'fetch', 'http', 'api'],
'超时': ['timeout', 'expire', 'delay'],
'重试': ['retry', 'repeat', 'again']
}
})AI 智能补充分析
当测试用例和 AST 分析都无法获取完整的文档信息时,可以启用 AI 智能补充功能,自动调用大模型 API 生成缺失的描述、语义标签和示例代码。
⚠️ 重要风险警告
┌─────────────────────────────────────────────────────────────────┐
│ AI 推导分析风险说明 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ⚠️ AI 分析基于静态源码推断,非事实基准 │
│ │
│ 1. AI 可能产生错误判断、幻觉或编造信息 │
│ 2. 生成的文档内容需要人工审核确认 │
│ 3. 不建议直接用于生产环境文档 │
│ 4. 分析对象为原始静态源码文件(.ts/.js),非 .d.ts 或生成文档 │
│ │
│ 请谨慎使用,务必审核 AI 生成的内容! │
│ │
└─────────────────────────────────────────────────────────────────┘分析对象说明
AI 分析基于原始静态源码文件(.ts、.js、.tsx、.jsx),而非:
- ❌ TypeScript 声明文件(
.d.ts) - ❌ 已生成的文档(
AI-Context.json) - ❌ 打包后的代码
这确保 AI 能够分析完整的代码实现,而非仅类型定义。
内置默认大模型
本插件内置了默认大模型,无需配置 API Key,开箱即用:
- 模型: Qwen/Qwen3.5-35B-A3B
- API:
https://api-ai.gitcode.com/v1/chat/completions - 优势: 无需注册账号、无需配置密钥、直接使用
配置方式
方式一:在 vitest.config.js 中配置
import { defineConfig } from 'vitest/config'
import DocReporter from 'vitest-plugin-ai-doc'
export default defineConfig({
test: {
reporters: [
new DocReporter({
outputFormat: 'dts',
outputDir: './docs',
enableAstAnalysis: true,
enableAI: true,
aiConfig: {
enabled: true
}
})
]
}
})方式二:使用 CLI 工具交互式配置
# 安装后直接运行
npx vitest-plugin-ai-doc
# 或全局安装后运行
npm install -g vitest-plugin-ai-doc
vitest-plugin-ai-docCLI 会依次提示输入:
- 最大 token 数(默认 500)
- 温度参数(默认 0.3)
AI 配置项
interface AIConfig {
enabled: boolean // 是否启用
maxTokens?: number // 最大 token 数,默认 500
temperature?: number // 温度参数,默认 0.3
}AI 分析流程
┌─────────────────────────────────────────────────────────────────┐
│ AI 分析流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. 测试用例分析 → 获取测试覆盖的导出信息 │
│ │ │
│ ▼ │
│ 2. AST 源码分析 → 获取未覆盖的导出信息 │
│ │ (分析原始 .ts/.js 源码文件) │
│ │ │
│ ▼ │
│ 3. 检测缺失信息 │
│ │ - 缺少描述 (description) │
│ │ - 缺少语义标签 (tags) │
│ │ - 缺少示例代码 (example) │
│ │ │
│ ▼ │
│ 4. AI 补充分析(仅对缺失信息的导出) │
│ │ - 读取原始源码实现 │
│ │ - 构建分析 prompt(包含源码片段) │
│ │ - 调用大模型 API │
│ │ - 解析返回结果 │
│ │ ⚠️ 结果需人工审核 │
│ │ │
│ ▼ │
│ 5. 合并结果 → 生成完整文档 │
│ │ - 标记 AI 生成的内容 │
│ │ - 输出警告信息 │
│ │ │
└─────────────────────────────────────────────────────────────────┘AI 分析示例
假设有一个未覆盖的导出(原始源码):
export function formatDate(date: Date, format: string): string {
const tokens: Record<string, () => string> = {
'YYYY': () => date.getFullYear().toString(),
'MM': () => (date.getMonth() + 1).toString().padStart(2, '0'),
'DD': () => date.getDate().toString().padStart(2, '0')
}
return format.replace(/YYYY|MM|DD/g, match => tokens[match]())
}AI 分析后会补充:
{
"_aiGenerated": true,
"name": "formatDate",
"type": "function",
"description": "格式化日期为指定格式的字符串",
"tags": ["日期", "格式化", "工具"],
"example": "formatDate(new Date(), 'YYYY-MM-DD')"
}⚠️ 注意:
_aiGenerated字段标记该内容由 AI 生成,需人工审核确认。
JSDoc 标签支持
基础标签
| 标签 | 说明 | 示例 |
|------|------|------|
| @description | 描述 | @description HTTP 客户端 |
| @semantic / @tags / @category | 语义标签 | @semantic HTTP, 请求, 网络 |
| @example / @usage | 示例代码 | @example const client = new HttpClient() |
| @param / @arg | 参数说明 | @param {string} url - 请求地址 |
| @returns / @return | 返回值 | @returns {Promise} 响应数据 |
| @throws / @exception | 异常 | @throws {Error} 网络错误 |
| @deprecated | 废弃说明 | @deprecated 请使用 HttpClient |
| @since | 版本 | @since 1.0.0 |
| @see | 参见 | @see HttpClient |
状态标签
| 标签 | 状态值 | 说明 |
|------|--------|------|
| @ignore | ignore | 忽略,不生成注释 |
| @important | important | 重要,AI 优先关注 |
| @outdated | outdated | 过时,需指定替代方案 |
| @new | new | 新版 API,替代旧版本 |
状态标签示例
/**
* HTTP 客户端类
* @important
* @semantic HTTP, 网络, 请求
* @example const client = new HttpClient({ timeout: 5000 })
*/
export class HttpClient { ... }
/**
* 旧版请求方法
* @outdated HttpClient.request
*/
export function request(url: string) { ... }
/**
* 新版请求方法
* @new[request]
*/
export class HttpClient {
request(url: string) { ... }
}
/**
* 内部方法
* @ignore
*/
function _internal() { ... }输出格式
💡 输出格式选择建议
┌─────────────────────────────────────────────────────────────────┐
│ 输出格式选择指南 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 🏆 强烈推荐: .d.ts 格式 │
│ │
│ 原因: │
│ 1. AI 编程助手(Cursor、Copilot、Claude 等)会 100% 读取此文件 │
│ 2. .d.ts 包含完整的类型定义,AI 能更好地理解代码结构 │
│ 3. 注释与类型定义紧密结合,上下文更清晰 │
│ 4. IDE 会自动加载,提供智能提示 │
│ │
│ ⚠️ AI-Context.json 的局限性: │
│ - 需要额外配置 AI 工具读取此文件 │
│ - 不是所有 AI 工具都支持自定义上下文文件 │
│ - 可能被忽略或优先级较低 │
│ │
│ 📋 建议: 优先选择 .d.ts,或选择 both 同时生成两种格式 │
│ │
└─────────────────────────────────────────────────────────────────┘格式对比
| 特性 | .d.ts | AI-Context.json | |------|-------|-----------------| | AI 读取率 | 100% | 需配置 | | 类型信息 | ✅ 完整 | ❌ 无 | | IDE 支持 | ✅ 原生 | ❌ 无 | | 结构化数据 | ❌ | ✅ | | 易于解析 | ❌ | ✅ |
AI-Context.json
{
"exports": {
"HttpClient": {
"kind": "class",
"desc": "HTTP 客户端类",
"tags": ["HTTP", "网络", "请求"],
"example": "const client = new HttpClient({ timeout: 5000 })",
"status": "important",
"props": {
"timeout": {
"desc": "超时时间(ms)",
"tags": ["配置"],
"example": "client.timeout = 10000",
"status": "normal"
}
},
"methods": {
"request": {
"desc": "发送请求",
"tags": ["请求", "异步"],
"example": "await client.request('/api/data')",
"status": "normal"
}
}
},
"request": {
"kind": "function",
"desc": "旧版请求方法",
"tags": [],
"example": "",
"status": "outdated",
"replacement": "HttpClient.request"
}
}
}.d.ts 注释
/** HTTP 客户端类 @tags HTTP, 网络, 请求 @example const client = new HttpClient({ timeout: 5000 }) @status important */
export class HttpClient {
/** 发送请求 @tags 请求, 异步 @example await client.request('/api/data') */
request(url: string): Promise<Response>
}
/** 旧版请求方法 @tags @example @status outdated @replacement HttpClient.request */
export function request(url: string): void注释格式兼容性
| 注释类型 | 支持 | 示例 |
|----------|------|------|
| /** ... */ JSDoc | ✅ | /** HTTP 客户端 */ |
| /* ... */ 块注释 | ✅ | /* 这是一个工具函数 */ |
| // 单行注释 | ✅ | // 发送请求 |
| 连续 // 注释 | ✅ | 多行 // 会合并 |
API
DocReporter
主 Reporter 类。
import { DocReporter } from 'vitest-plugin-ai-doc'
const reporter = new DocReporter({
outputFormat: 'ai-context',
outputDir: './docs'
})其他导出
// 类型
export type {
ReporterOptions,
GeneratedComment,
TestMetadata,
TargetLevel,
ExportStatus,
OutputFormat,
SourceExport,
JSDocInfo,
JSDocTag,
AIConfig
} from 'vitest-plugin-ai-doc'
// 工具函数
export { TestCollector } from 'vitest-plugin-ai-doc'
export { CommentAggregator, formatComment } from 'vitest-plugin-ai-doc'
export { DtsInjector, findDtsFiles } from 'vitest-plugin-ai-doc'
export { SourceAnalyzer } from 'vitest-plugin-ai-doc'
export { CacheManager, computeFileHash } from 'vitest-plugin-ai-doc'
export { BundleAnalyzer } from 'vitest-plugin-ai-doc'
export { CoverageMerger } from 'vitest-plugin-ai-doc'
export { AIService, needsAIAnalysis, mergeAIResult } from 'vitest-plugin-ai-doc'
export { PreviewManager } from 'vitest-plugin-ai-doc'CLI 命令
安装后可使用命令行工具:
vitest-plugin-ai-doc [命令]命令
| 命令 | 说明 |
|------|------|
| analyze | 运行 AI 文档补充分析(默认命令) |
| confirm | 确认预览文件中的内容并写入 |
| status | 查看当前预览状态 |
| reject | 拒绝所有 AI 生成内容并删除预览文件 |
| help | 显示帮助信息 |
选项
| 选项 | 说明 |
|------|------|
| -h, --help | 显示帮助信息 |
| --preview-only | 只生成预览文件,不进入交互模式 |
示例
# 运行 AI 分析(交互模式)
vitest-plugin-ai-doc
# 同上
vitest-plugin-ai-doc analyze
# 只生成预览文件
vitest-plugin-ai-doc analyze --preview-only
# 确认预览内容
vitest-plugin-ai-doc confirm
# 查看预览状态
vitest-plugin-ai-doc status
# 拒绝所有内容
vitest-plugin-ai-doc reject
# 显示帮助
vitest-plugin-ai-doc help预览确认流程
AI 分析完成后,会进入预览确认模式:
┌─────────────────────────────────────────────────────────────────┐
│ 📋 预览确认模式 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 请选择操作: │
│ [A] 接受全部 AI 生成内容 │
│ [S] 选择性确认(逐项审核) │
│ [V] 查看预览文件后手动确认 │
│ [Q] 取消,不保存任何内容 │
│ │
└─────────────────────────────────────────────────────────────────┘选择性确认示例
┌──────────────────────────────────────────────────────────────────┐
│ [1/3] formatDate │
├──────────────────────────────────────────────────────────────────┤
│ 描述: │
│ 原始: (空) │
│ AI生成: 格式化日期为指定格式的字符串 [AI] │
│ │
│ 标签: │
│ 原始: (空) │
│ AI生成: 日期, 格式化, 工具 [AI] │
│ │
│ 示例: │
│ 原始: (空) │
│ AI生成: formatDate(new Date(), 'YYYY-MM-DD') [AI] │
│ │
│ 状态: ⏳ 待确认 │
└──────────────────────────────────────────────────────────────────┘
操作: [y] 接受 [n] 拒绝 [s] 跳过剩余 [q] 取消全部
请选择: 预览文件结构
预览文件保存在 {outputDir}/ai-preview.json:
{
"_meta": {
"warning": "AI 生成内容基于静态源码推断,可能存在错误,请人工审核后使用",
"generatedAt": "2024-01-15T10:30:00Z",
"modelName": "deepseek-chat"
},
"stats": {
"total": 3,
"newDescription": 3,
"newTags": 2,
"newExample": 1
},
"pending": [
{
"target": "formatDate",
"type": "function",
"original": { "description": "", "tags": [], "example": "" },
"aiGenerated": {
"description": "格式化日期为指定格式的字符串",
"tags": ["日期", "格式化", "工具"],
"example": "formatDate(new Date(), 'YYYY-MM-DD')"
},
"accepted": null
}
]
}开发
# 安装依赖
npm install
# 类型检查
npm run typecheck
# 构建
npm run build
# 开发模式(监听变化)
npm run devLicense
MIT
