UNPKG

autosnippet

Version:

Extract code patterns into a knowledge base for AI coding assistants

105 lines (104 loc) 3.12 kB
/** * SourceFileCollector — 递归收集项目源文件 * * 从 GuardHandler 提取的公共工具,供 ComplianceReporter / guard:ci / guard:staged 复用 */ import { readdir, readFile } from 'node:fs/promises'; import { extname, join } from 'node:path'; import { LanguageService } from '../../shared/LanguageService.js'; /** 支持审计的源文件扩展名 — 委托给 LanguageService */ export const SOURCE_EXTS = LanguageService.sourceExts; /** 跳过的目录 */ const SKIP_DIRS = new Set([ 'node_modules', '.git', 'build', 'DerivedData', 'Pods', '.build', 'vendor', 'dist', '.next', 'Carthage', 'xcuserdata', '__pycache__', // Rust 'target', '.cargo', // Python 虚拟环境 'venv', '.venv', 'env', // JVM 构建工具 '.gradle', '.mvn', // 通用构建/输出目录 'out', 'coverage', '.turbo', '.parcel-cache', ]); /** * 递归收集目录下所有源文件路径 * @param dir 根目录 * @param [options.extensions] 允许的扩展名(默认 SOURCE_EXTS) * @param [options.skipDirs] 跳过的目录名(默认 SKIP_DIRS) * @param [options.maxFiles] 最大文件数量(默认无限制) * @returns 文件路径列表 */ export async function collectSourceFiles(dir, options = {}) { const { extensions = SOURCE_EXTS, skipDirs = SKIP_DIRS, maxFiles = Infinity } = options; const files = []; async function walk(currentDir) { if (files.length >= maxFiles) { return; } let entries; try { entries = await readdir(currentDir, { withFileTypes: true }); } catch { return; // 权限不足等情况跳过 } for (const entry of entries) { if (files.length >= maxFiles) { return; } if (entry.name.startsWith('.') && entry.name !== '.') { continue; } const fullPath = join(currentDir, entry.name); if (entry.isDirectory()) { if (!skipDirs.has(entry.name)) { await walk(fullPath); } } else if (entry.isFile() && extensions.has(extname(entry.name).toLowerCase())) { files.push(fullPath); } } } await walk(dir); return files; } /** * 收集源文件并读取内容(带测试文件标记) * @param dir 根目录 * @param options collectSourceFiles 选项 * @returns { path, content, isTest }[] */ export async function collectSourceFilesWithContent(dir, options = {}) { const paths = await collectSourceFiles(dir, options); const results = []; for (const filePath of paths) { try { const content = await readFile(filePath, 'utf-8'); results.push({ path: filePath, content, isTest: LanguageService.isTestFile(filePath) }); } catch { // 读取失败跳过 } } return results; } export default { collectSourceFiles, collectSourceFilesWithContent, SOURCE_EXTS };