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
JavaScript
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
;