UNPKG

@johnlindquist/file-forge

Version:

File Forge is a powerful CLI tool for deep analysis of codebases, generating markdown reports to feed AI reasoning models.

116 lines (113 loc) 4.67 kB
import madge from "madge"; import { resolve, dirname, basename } from "node:path"; import { getFileContent } from "./fileUtils.js"; // Recursively build a tree string from the dependency graph function buildDependencyTree(deps, current, visited = new Set(), prefix = "", flags = {}) { let treeStr = prefix + current + "\n"; visited.add(current); const children = deps[current] || []; children.forEach((child, index) => { const isLast = index === children.length - 1; const connector = isLast ? "└── " : "├── "; if (visited.has(child)) { // Use conditional whitespace for child spacing const spacing = flags.whitespace ? " " : " "; treeStr += prefix + spacing + connector + child + " (circular)\n"; } else { // Use conditional whitespace for indentation const indentation = flags.whitespace ? " " : " "; const divider = flags.whitespace ? "│ " : "│ "; const newPrefix = prefix + (isLast ? indentation : divider); treeStr += prefix + connector + buildDependencyTree(deps, child, new Set(visited), newPrefix, flags).trimStart(); } }); return treeStr; } // Gather file contents for the files in the dependency graph async function gatherGraphFiles(files, maxSize) { const fileContents = {}; const seenPaths = new Set(); for (const file of files) { // Get the original relative path for storing in the result const relativePath = file.replace(process.cwd() + "/", ""); if (seenPaths.has(relativePath)) continue; seenPaths.add(relativePath); try { const content = await getFileContent(file, maxSize, relativePath, { skipHeader: false }); if (content === null) { console.log(`[DEBUG] File ignored by getFileContent: ${file}`); continue; } fileContents[relativePath] = content; } catch (error) { console.error(`[DEBUG] Error reading file ${file}:`, error); fileContents[relativePath] = `================================\nFile: ${relativePath}\n================================\n[Error reading file]`; } } // Join all file contents with newlines let contentStr = ""; for (const filePath in fileContents) { contentStr += fileContents[filePath] + "\n\n"; } return { fileContents, contentStr }; } // Main function to ingest the dependency graph export async function ingestGraph(entryFile, flags) { console.log("[DEBUG] Starting ingestGraph with entry file:", entryFile); const resolvedEntry = resolve(entryFile); const baseDir = dirname(resolvedEntry); console.log("[DEBUG] Resolved entry path:", resolvedEntry); console.log("[DEBUG] Base directory:", baseDir); let madgeResult; try { console.log("[DEBUG] Running madge analysis..."); madgeResult = await madge(resolvedEntry, { fileExtensions: ["js", "jsx", "ts", "tsx"], detectiveOptions: { es6: { mixedImports: true }, ts: { mixedImports: true }, }, }); console.log("[DEBUG] Madge analysis complete"); console.log("[DEBUG] Raw madge result:", madgeResult); } catch (error) { console.error("[DEBUG] Madge failed:", error); throw error; } const dependencies = madgeResult.obj(); console.log("[DEBUG] Dependencies object:", JSON.stringify(dependencies, null, 2)); // Get all unique files from the dependency tree const allFiles = new Set(); for (const [file, deps] of Object.entries(dependencies)) { allFiles.add(resolve(baseDir, file)); for (const dep of deps) { allFiles.add(resolve(baseDir, dep)); } } console.log("[DEBUG] All files found:", Array.from(allFiles)); // Build tree structure const treeStr = buildDependencyTree(dependencies, basename(entryFile), new Set(), "", flags); console.log("[DEBUG] Generated tree structure:\n", treeStr); // Gather file contents const { contentStr } = await gatherGraphFiles(Array.from(allFiles), flags.maxSize || 10 * 1024 * 1024); // Build summary const summary = `# Dependency Graph Analysis ## Entry Point ${entryFile} ## Dependency Tree \`\`\` ${treeStr} \`\`\` ## Files Analyzed Files analyzed: ${Array.from(allFiles).length} files were analyzed in the dependency graph. `; return { summary, treeStr, contentStr }; } //# sourceMappingURL=graph.js.map