UNPKG

spaider

Version:

Deterministic-first AI code assistant that crawls your codebase to implement changes using open source LLMs

104 lines 4.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tree = void 0; const child_process_1 = require("child_process"); var Tree; (function (Tree) { async function generateTree(projectRoot = process.cwd(), options = {}) { const { types = ["ts", "tsx", "js", "jsx", "json", "md", "yaml", "yml"], exclude = ["node_modules", ".git", "dist", "build", ".next", "coverage"], maxFiles = 100, } = options; const files = await getRipgrepFiles(projectRoot, types, exclude, maxFiles); return buildTree(files); } Tree.generateTree = generateTree; async function getRipgrepFiles(projectRoot, types, exclude, maxFiles) { return new Promise((resolve, reject) => { // Use glob patterns for all file types to avoid ripgrep type issues const typeArgs = types.flatMap((type) => ["-g", `*.${type}`]); const args = [ "--files", "--sort", "path", ...typeArgs, ...exclude.flatMap((path) => ["-g", `!${path}`]), ]; const rg = (0, child_process_1.spawn)("rg", args, { cwd: projectRoot, }); let output = ""; rg.stdout.on("data", (data) => { output += data.toString(); }); rg.stderr.on("data", (data) => { console.error(`rg stderr: ${data}`); }); rg.on("close", (code) => { if (code === 0 || code === 1) { // 1 is "no matches" which is ok const files = output.trim().split("\n").filter(Boolean); resolve(files.slice(0, maxFiles)); } else { reject(new Error(`ripgrep exited with code ${code}`)); } }); }); } function buildTree(files) { const tree = new Map(); // Initialize root tree.set("", { dirs: new Set(), files: new Set() }); // Build directory structure files.forEach((file) => { const parts = file.split("/"); // Add all directory levels for (let i = 0; i < parts.length; i++) { const currentPath = parts.slice(0, i).join("/"); const currentName = parts[i]; if (!tree.has(currentPath)) { tree.set(currentPath, { dirs: new Set(), files: new Set() }); } if (i === parts.length - 1) { // It's a file tree.get(currentPath).files.add(currentName); } else { // It's a directory tree.get(currentPath).dirs.add(currentName); } } }); return formatTree(tree, "", "", new Set()); } function formatTree(tree, currentPath, prefix, processedPaths) { if (processedPaths.has(currentPath)) return ""; processedPaths.add(currentPath); const node = tree.get(currentPath); if (!node) return ""; const lines = []; const dirs = Array.from(node.dirs).sort(); const files = Array.from(node.files).sort(); const totalItems = dirs.length + files.length; // Process directories first dirs.forEach((dir, index) => { const isLast = index === totalItems - 1 && files.length === 0; const connector = isLast ? "└── " : "├── "; const nextPrefix = prefix + (isLast ? " " : "│ "); lines.push(`${prefix}${connector}${dir}/`); const childPath = currentPath ? `${currentPath}/${dir}` : dir; const childTree = formatTree(tree, childPath, nextPrefix, processedPaths); if (childTree) { lines.push(childTree); } }); // Process files files.forEach((file, index) => { const isLast = index === files.length - 1; const connector = isLast ? "└── " : "├── "; lines.push(`${prefix}${connector}${file}`); }); return lines.join("\n"); } })(Tree || (exports.Tree = Tree = {})); //# sourceMappingURL=tree.js.map