fix(glob): include top-level files when recursively listing ignored directories

- Fix issue where files at root level of directories in DIRS_TO_IGNORE were excluded
- Add explicit include patterns (* and **/*) when targeting ignored directories
- Modify exclusion pattern to use !*/dir/** instead of !**/dir/** for target directory
- Add comprehensive test case for .roo/temp scenario

Fixes #5176
This commit is contained in:
MuriloFP 2025-07-10 16:54:32 -03:00
parent d518f27ecc
commit a1252f8994
2 changed files with 87 additions and 5 deletions

View file

@ -336,4 +336,61 @@ describe("hidden directory exclusion", () => {
expect(hasTasksDir).toBe(true)
expect(hasContextDir).toBe(true)
})
it("should include top-level files when recursively listing a hidden directory that's also in DIRS_TO_IGNORE", async () => {
// This test specifically addresses the bug where files at the root level of .roo/temp
// were being excluded when using recursive listing
const mockSpawn = vi.mocked(childProcess.spawn)
const mockProcess = {
stdout: {
on: vi.fn((event, callback) => {
if (event === "data") {
// Simulate files that should be found in .roo/temp
setTimeout(() => {
callback(".roo/temp/teste1.md\n")
callback(".roo/temp/22/test2.md\n")
}, 10)
}
}),
},
stderr: {
on: vi.fn(),
},
on: vi.fn((event, callback) => {
if (event === "close") {
setTimeout(() => callback(0), 20)
}
}),
kill: vi.fn(),
}
mockSpawn.mockReturnValue(mockProcess as any)
// Mock directory listing for .roo/temp
const mockReaddir = vi.fn()
vi.mocked(fs.promises).readdir = mockReaddir
mockReaddir.mockResolvedValueOnce([{ name: "22", isDirectory: () => true, isSymbolicLink: () => false }])
// Call listFiles targeting .roo/temp (which is both hidden and in DIRS_TO_IGNORE)
const [files] = await listFiles("/test/.roo/temp", true, 100)
// Verify ripgrep was called with correct arguments
const [rgPath, args] = mockSpawn.mock.calls[0]
expect(args).toContain("--no-ignore-vcs")
expect(args).toContain("--no-ignore")
// Check for the inclusion patterns that should be added
expect(args).toContain("-g")
const gIndex = args.indexOf("-g")
expect(args[gIndex + 1]).toBe("*")
// Verify that both top-level and nested files are included
const fileNames = files.map((f) => path.basename(f))
expect(fileNames).toContain("teste1.md")
expect(fileNames).toContain("test2.md")
// Ensure the top-level file is actually included
const topLevelFile = files.find((f) => f.endsWith("teste1.md"))
expect(topLevelFile).toBeTruthy()
})
})

View file

@ -208,23 +208,48 @@ function buildRecursiveArgs(dirPath: string): string[] {
// (ripgrep does this automatically)
// Check if we're explicitly targeting a hidden directory
// We need to check all parts of the path, not just the basename
const pathParts = dirPath.split(path.sep).filter((part) => part !== "")
const isTargetingHiddenDir = pathParts.some((part) => part.startsWith("."))
// Get the target directory name to check if it's in the ignore list
const targetDirName = path.basename(dirPath)
const isTargetingHiddenDir = targetDirName.startsWith(".")
const isTargetInIgnoreList = DIRS_TO_IGNORE.includes(targetDirName)
// If targeting a hidden directory or a directory in the ignore list,
// use special handling to ensure all files are shown
if (isTargetingHiddenDir || isTargetInIgnoreList) {
args.push("--no-ignore-vcs")
args.push("--no-ignore")
// When targeting an ignored directory, we need to be careful with glob patterns
// Add a pattern to explicitly include files at the root level
args.push("-g", "*")
args.push("-g", "**/*")
}
// Apply directory exclusions for recursive searches
for (const dir of DIRS_TO_IGNORE) {
// Special handling for hidden directories pattern
if (dir === ".*") {
// Only exclude hidden directories if we're not explicitly targeting one
// This allows explicitly targeted hidden directories to be processed
// while excluding them from general recursive searches
// If we're explicitly targeting a hidden directory, don't exclude hidden files/dirs
// This allows the target hidden directory and all its contents to be listed
if (!isTargetingHiddenDir) {
// Not targeting hidden dir: exclude all hidden directories
args.push("-g", `!**/.*/**`)
}
// If targeting hidden dir: don't add any exclusion for hidden directories
continue
}
args.push("-g", `!**/${dir}/**`)
// When targeting a directory that's in the ignore list, modify the exclusion pattern
// to only exclude nested directories with the same name, not the root level
if (dir === targetDirName && isTargetInIgnoreList) {
// Only exclude subdirectories, not files at the root level
args.push("-g", `!*/${dir}/**`)
} else {
args.push("-g", `!**/${dir}/**`)
}
}
return args
}