git-yike-logger-hook
Version:
A TypeScript Git hook plugin for automatically generating commit logs with TODO/WIP comment scanning
468 lines (359 loc) • 10.9 kB
Markdown
```
git-logger-hook/
├── src/
│ ├── types.ts
│ ├── git-utils.ts
│ ├── comment-scanner.ts
│ ├── logger.ts
│ ├── hook.ts
│ ├── install.ts
│ └── index.ts
├── dist/
├── .git-logs/
├── package.json
├── tsconfig.json
├── .gitignore
├── README.md
├── DEVELOPMENT.md
└── example.js
```
```typescript
export class GitLogger {
private gitUtils: GitUtils;
private commentScanner: CommentScanner;
constructor() {
this.gitUtils = new GitUtils();
this.commentScanner = new CommentScanner();
}
}
```
**优势:**
- 职责分离,每个类只负责特定功能
- 易于测试和维护
- 支持独立扩展各个组件
```typescript
// 不同的注释扫描策略
private todoPatterns = [
/\/\/\s*TODO\s*:?\s*(.+)/gi,
/\/\*\s*TODO\s*:?\s*(.+?)\s*\*\//gi,
// ...
];
```
**优势:**
- 易于添加新的注释格式
- 扫描逻辑与业务逻辑分离
- 支持运行时切换策略
```typescript
// 根据文件类型创建不同的处理器
private isJavaScriptFile(filePath: string): boolean {
const ext = path.extname(filePath).toLowerCase();
return this.supportedExtensions.includes(ext);
}
```
```typescript
async generateLog(): Promise<void> {
try {
// 并行执行多个异步操作
const [commitInfo, branch, remote, fileChanges] = await Promise.all([
this.gitUtils.getCurrentCommitInfo(),
this.gitUtils.getCurrentBranch(),
this.gitUtils.getRemoteInfo(),
this.gitUtils.getFileChanges()
]);
// 顺序执行依赖操作
const comments = await this.commentScanner.scanChangedFiles(fileChanges);
} catch (error) {
// 统一错误处理
}
}
```
**关键点:**
- 使用 `Promise.all()` 并行执行独立操作
- 使用 `await` 顺序执行依赖操作
- 统一的错误处理机制
```typescript
// 分级错误处理
try {
const result = await riskyOperation();
return result;
} catch (error) {
if (error instanceof SpecificError) {
// 特定错误处理
console.warn('特定错误:', error.message);
return defaultValue;
} else {
// 通用错误处理
throw new Error(`操作失败: ${error}`);
}
}
```
**策略:**
- 可恢复错误:记录警告,使用默认值
- 致命错误:抛出异常,终止执行
- 用户友好:提供清晰的错误信息
```typescript
// 安全的文件操作
private async ensureLogDirectory(): Promise<void> {
if (!fs.existsSync(this.config.logDir)) {
fs.mkdirSync(this.config.logDir, { recursive: true });
}
}
// 原子性写入
private async writeLogFile(filePath: string, content: string): Promise<void> {
const tempFile = filePath + '.tmp';
fs.writeFileSync(tempFile, content, 'utf8');
fs.renameSync(tempFile, filePath);
}
```
**关键点:**
- 使用 `recursive: true` 创建嵌套目录
- 使用临时文件确保原子性写入
- 检查文件存在性避免重复操作
```typescript
// 只扫描变更的文件
async scanChangedFiles(changedFiles: FileChanges): Promise<Comments> {
const filesToScan = [...changedFiles.added, ...changedFiles.modified]
.filter(file => this.isJavaScriptFile(file));
// 并行扫描多个文件
const results = await Promise.all(
filesToScan.map(file => this.scanFile(file))
);
return this.mergeResults(results);
}
```
```typescript
// 流式处理大文件
private async scanLargeFile(filePath: string): Promise<Comments> {
return new Promise((resolve, reject) => {
const stream = fs.createReadStream(filePath, { encoding: 'utf8' });
let lineNumber = 0;
const comments: CodeComment[] = [];
stream.on('line', (line) => {
lineNumber++;
const found = this.extractComments(line, lineNumber);
comments.push(...found);
});
stream.on('end', () => resolve({ todos: comments, wips: [] }));
stream.on('error', reject);
});
}
```
```typescript
// 简单的内存缓存
private fileCache = new Map<string, { content: string; mtime: number }>();
private async getFileContent(filePath: string): Promise<string> {
const stat = fs.statSync(filePath);
const cac
hed = this.fileCache.get(filePath);
if (cached && cached.mtime === stat.mtime.getTime()) {
return cached.content;
}
const content = fs.readFileSync(filePath, 'utf8');
this.fileCache.set(filePath, { content, mtime: stat.mtime.getTime() });
return content;
}
```
```typescript
// 使用 Jest 进行单元测试
describe('CommentScanner', () => {
test('应该正确提取 TODO 注释', async () => {
const scanner = new CommentScanner();
const result = await scanner.scanFile('test.js');
expect(result.todos).toHaveLength(2);
expect(result.todos[0].content).toBe('实现这个功能');
});
});
```
```typescript
// 测试完整的日志生成流程
describe('GitLogger 集成测试', () => {
test('应该生成完整的日志', async () => {
const logger = new GitLogger();
await logger.generateLog();
const logs = await logger.getAllLogs();
expect(logs).toHaveLength(1);
expect(logs[0].comments.todos).toBeDefined();
});
});
```
```bash
git init test-repo
cd test-repo
npm install ../git-logger-hook
node dist/install.js install
echo "// TODO: 测试注释" > test.js
git add test.js
git commit -m "测试提交"
ls -la .git-logs/
cat .git-logs/*.json | jq '.comments.todos'
```
## 调试技巧
### 1. 日志级别
```typescript
enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3,
}
class Logger {
private level: LogLevel = LogLevel.INFO;
debug(message: string, ...args: any[]) {
if (this.level <= LogLevel.DEBUG) {
console.log(`[DEBUG] ${message}`, ...args);
}
}
}
```
### 2. 性能监控
```typescript
// 性能计时器
class PerformanceTimer {
private timers = new Map<string, number>();
start(label: string) {
this.timers.set(label, Date.now());
}
end(label: string): number {
const start = this.timers.get(label);
if (!start) return 0;
const duration = Date.now() - start;
console.log(`${label}: ${duration}ms`);
return duration;
}
}
```
### 3. 错误追踪
```typescript
// 详细的错误信息
try {
await riskyOperation();
} catch (error) {
console.error('操作失败:', {
error: error.message,
stack: error.stack,
timestamp: new Date().toISOString(),
context: { file: __filename, line: __line },
});
}
```
## 扩展指南
### 1. 添加新的注释类型
```typescript
// 1. 在 types.ts 中定义新类型
export type CommentType = 'TODO' | 'WIP' | 'FIXME' | 'NOTE';
// 2. 在 comment-scanner.ts 中添加模式
private fixmePatterns = [
/\/\/\s*FIXME\s*:?\s*(.+)/gi,
/\/\*\s*FIXME\s*:?\s*(.+?)\s*\*\//gi
];
// 3. 更新扫描逻辑
private extractMatches(text: string, pattern: RegExp, type: CommentType) {
// 实现新的提取逻辑
}
```
### 2. 添加新的文件类型
```typescript
// 在 comment-scanner.ts 中扩展支持的文件类型
private supportedExtensions = [
'.js', '.jsx', '.ts', '.tsx',
'.vue', '.svelte', '.py', '.java' // 新增支持
];
```
### 3. 添加新的 Git 操作
```typescript
// 在 git-utils.ts 中添加新方法
async getCommitStats(): Promise<{
insertions: number;
deletions: number;
filesChanged: number;
}> {
const diff = await this.git.diff(['--stat']);
// 解析 diff 统计信息
return this.parseDiffStats(diff);
}
```
## 部署指南
### 1. 本地开发
```bash
# 克隆项目
git clone <repository-url>
cd git-logger-hook
# 安装依赖
npm install
# 开发模式
npm run dev
# 测试
npm test
```
### 2. 生产部署
```bash
# 编译项目
npm run build
# 安装到目标仓库
cd /path/to/target/repo
npm install /path/to/git-logger-hook
node node_modules/git-logger-hook/dist/install.js install
```
### 3. 全局安装
```bash
# 发布到 npm
npm publish
# 全局安装
npm install -g git-logger-hook
# 在任何仓库中使用
git-logger-hook install
```
## 贡献指南
### 1. 代码规范
- 使用 TypeScript 严格模式
- 遵循 ESLint 规则
- 添加完整的 JSDoc 注释
- 保持 80% 以上的测试覆盖率
### 2. 提交规范
```bash
# 使用约定式提交
git commit -m "feat: 添加新的注释类型支持"
git commit -m "fix: 修复文件扫描性能问题"
git commit -m "docs: 更新 API 文档"
```
### 3. 拉取请求
- 提供详细的变更说明
- 包含相关的测试用例
- 更新文档和示例
- 确保所有测试通过
## 常见问题
### Q: 为什么选择 TypeScript?
A: TypeScript 提供类型安全、更好的 IDE 支持、编译时错误检查,特别适合构建工具类项目。
### Q: 为什么不直接使用 Git 命令?
A: simple-git 提供了统一的 Promise 接口,简化了异步操作,并且内置了错误处理机制。
### Q: 如何提高扫描性能?
A: 只扫描变更的文件、使用并行处理、实现文件缓存、跳过大型目录。
### Q: 如何扩展支持更多文件类型?
A: 在 `comment-scanner.ts` 中添加新的文件扩展名和对应的注释模式。
### Q: 如何处理大型项目?
A: 实现增量扫描、文件缓存、并行处理,并考虑添加配置文件来排除特定目录。