const path = require("node:path"); const { execFile } = require("node:child_process"); const { promisify } = require("node:util"); const { glob } = require("glob"); const { loadIgnore } = require("./ignoreRules.js"); const pExecFile = promisify(execFile); async function isGitRepo(rootDir) { try { const { stdout } = await pExecFile("git", [ "rev-parse", "--is-inside-work-tree", ], { cwd: rootDir }); return String(stdout || "").toString().trim() === "true"; } catch { return false; } } async function gitListFiles(rootDir) { try { const { stdout } = await pExecFile("git", [ "ls-files", "-co", "--exclude-standard", ], { cwd: rootDir }); return String(stdout || "") .split(/\r?\n/) .map((s) => s.trim()) .filter(Boolean); } catch { return []; } } /** * Discover files under rootDir. * - Prefer git ls-files when available for speed/correctness * - Fallback to glob and apply unified ignore rules * @param {string} rootDir * @param {object} [options] * @param {boolean} [options.preferGit=true] * @returns {Promise} absolute file paths */ async function discoverFiles(rootDir, options = {}) { const { preferGit = true } = options; const { filter } = await loadIgnore(rootDir); // Try git first if (preferGit && await isGitRepo(rootDir)) { const relFiles = await gitListFiles(rootDir); const filteredRel = relFiles.filter((p) => filter(p)); return filteredRel.map((p) => path.resolve(rootDir, p)); } // Glob fallback const globbed = await glob("**/*", { cwd: rootDir, nodir: true, dot: true, follow: false, }); const filteredRel = globbed.filter((p) => filter(p)); return filteredRel.map((p) => path.resolve(rootDir, p)); } module.exports = { discoverFiles, };