UNPKG

git-yike-logger-hook

Version:

A TypeScript Git hook plugin for automatically generating commit logs with TODO/WIP comment scanning

234 lines 8.13 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.CommentScanner = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); /** * JavaScript 文件注释扫描器 */ class CommentScanner { constructor() { this.supportedExtensions = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte']; this.todoPatterns = [ /\/\/\s*TODO\s*:?\s*(.+)/gi, /\/\*\s*TODO\s*:?\s*(.+?)\s*\*\//gi, /\/\/\s*@todo\s*:?\s*(.+)/gi, /\/\*\s*@todo\s*:?\s*(.+?)\s*\*\//gi ]; this.wipPatterns = [ /\/\/\s*WIP\s*:?\s*(.+)/gi, /\/\*\s*WIP\s*:?\s*(.+?)\s*\*\//gi, /\/\/\s*@wip\s*:?\s*(.+)/gi, /\/\*\s*@wip\s*:?\s*(.+?)\s*\*\//gi, /\/\/\s*WORK\s*IN\s*PROGRESS\s*:?\s*(.+)/gi, /\/\*\s*WORK\s*IN\s*PROGRESS\s*:?\s*(.+?)\s*\*\//gi ]; } /** * 扫描指定目录下的所有 JavaScript 文件,提取 TODO 和 WIP 注释 */ async scanDirectory(dirPath) { const todos = []; const wips = []; try { const files = await this.getAllJavaScriptFiles(dirPath); for (const filePath of files) { const fileComments = await this.scanFile(filePath); todos.push(...fileComments.todos); wips.push(...fileComments.wips); } } catch (error) { console.warn('扫描目录失败:', error); } return { todos, wips }; } /** * 扫描单个文件 */ async scanFile(filePath) { const todos = []; const wips = []; try { if (!fs.existsSync(filePath)) { return { todos, wips }; } const content = fs.readFileSync(filePath, 'utf8'); const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; const lineNumber = i + 1; // 扫描 TODO 注释 for (const pattern of this.todoPatterns) { const matches = this.extractMatches(line, pattern, 'TODO'); for (const match of matches) { todos.push({ type: 'TODO', content: match.content, filePath: this.normalizePath(filePath), lineNumber, fullLine: line.trim() }); } } // 扫描 WIP 注释 for (const pattern of this.wipPatterns) { const matches = this.extractMatches(line, pattern, 'WIP'); for (const match of matches) { wips.push({ type: 'WIP', content: match.content, filePath: this.normalizePath(filePath), lineNumber, fullLine: line.trim() }); } } } } catch (error) { console.warn(`扫描文件失败 ${filePath}:`, error); } return { todos, wips }; } /** * 获取目录下所有 JavaScript 文件 */ async getAllJavaScriptFiles(dirPath) { const files = []; const scanDir = (currentPath) => { try { const items = fs.readdirSync(currentPath); for (const item of items) { const fullPath = path.join(currentPath, item); const stat = fs.statSync(fullPath); if (stat.isDirectory()) { // 跳过 node_modules、.git、dist 等目录 if (this.shouldSkipDirectory(item)) { continue; } scanDir(fullPath); } else if (stat.isFile()) { const ext = path.extname(item).toLowerCase(); if (this.supportedExtensions.includes(ext)) { files.push(fullPath); } } } } catch (error) { console.warn(`扫描目录失败 ${currentPath}:`, error); } }; scanDir(dirPath); return files; } /** * 判断是否应该跳过某个目录 */ shouldSkipDirectory(dirName) { const skipDirs = [ 'node_modules', '.git', 'dist', 'build', 'coverage', '.next', '.nuxt', '.vuepress', '.vite', 'target', 'out', '.idea', '.vscode', '.vs' ]; return skipDirs.includes(dirName) || dirName.startsWith('.'); } /** * 从文本中提取匹配的注释 */ extractMatches(text, pattern, type) { const matches = []; let match; // 重置正则表达式的 lastIndex pattern.lastIndex = 0; while ((match = pattern.exec(text)) !== null) { const content = match[1] ? match[1].trim() : ''; if (content) { matches.push({ content }); } } return matches; } /** * 标准化文件路径(使用正斜杠) */ normalizePath(filePath) { return filePath.replace(/\\/g, '/'); } /** * 扫描工作区中变更的 JavaScript 文件 */ async scanChangedFiles(changedFiles) { const todos = []; const wips = []; // 只扫描新增和修改的文件 const filesToScan = [...changedFiles.added, ...changedFiles.modified] .filter(file => this.isJavaScriptFile(file)); for (const file of filesToScan) { try { const fileComments = await this.scanFile(file); todos.push(...fileComments.todos); wips.push(...fileComments.wips); } catch (error) { console.warn(`扫描变更文件失败 ${file}:`, error); } } return { todos, wips }; } /** * 判断是否为 JavaScript 文件 */ isJavaScriptFile(filePath) { const ext = path.extname(filePath).toLowerCase(); return this.supportedExtensions.includes(ext); } } exports.CommentScanner = CommentScanner; //# sourceMappingURL=comment-scanner.js.map