Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions common/src/__tests__/project-file-tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,28 @@ describe('getProjectFileTree', () => {
expect(filePaths).toEqual(['app/src/main/Inventory.kt'])
})

it('does not stat ignore files when none exist in directory entries', async () => {
const root = '/repo'
const fs = createFsWithFiles(root, ['src/index.ts', 'src/util.ts'])
const statCalls: string[] = []
const originalStat = fs.stat
;(fs as any).stat = async (p: any, ...args: any[]) => {
statCalls.push(String(p))
return (originalStat as any)(p, ...args)
}

await getProjectFileTree({ projectRoot: root, fs })

// None of the stat calls should be for ignore files
const ignoreStatCalls = statCalls.filter(
(p) =>
p.endsWith('.gitignore') ||
p.endsWith('.codebuffignore') ||
p.endsWith('.manicodeignore'),
)
expect(ignoreStatCalls).toHaveLength(0)
})

it('prunes directories ignored by a rule in a nested .gitignore', async () => {
const root = '/repo'
const fs = createFsWithFiles(root, [
Expand Down
43 changes: 30 additions & 13 deletions common/src/project-file-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,20 +128,34 @@ export async function getProjectFileTree(params: {
]
let totalFiles = 0
let dirsScanned = 0
let queueIndex = 0

while (queue.length > 0 && totalFiles < maxFiles && dirsScanned < maxDirs) {
const { node, fullPath, ignores, depth } = queue.shift()!
while (queueIndex < queue.length && totalFiles < maxFiles && dirsScanned < maxDirs) {
const { node, fullPath, ignores, depth } = queue[queueIndex++]
dirsScanned++
const dirIgnores = [
...ignores,
{
base: toPosixPath(path.relative(projectRoot, fullPath)),
ig: await parseGitignore({ fullDirPath: fullPath, fs }),
},
]

try {
const files = await fs.readdir(fullPath)
const filesSet = new Set(files)

let dirIgnores = ignores
const hasIgnoreFile = PROJECT_IGNORE_FILES.some((fileName) =>
filesSet.has(fileName),
)
if (hasIgnoreFile) {
dirIgnores = [
...ignores,
{
base: toPosixPath(path.relative(projectRoot, fullPath)),
ig: await parseGitignore({
fullDirPath: fullPath,
fs,
directoryEntries: filesSet,
}),
},
]
}

for (const file of files) {
if (totalFiles >= maxFiles) break

Expand Down Expand Up @@ -199,13 +213,15 @@ async function parseGitignoreWithMode(params: {
fullDirPath: string
fs: CodebuffFileSystem
throwOnReadError: boolean
directoryEntries?: Set<string>
}): Promise<ignore.Ignore> {
const { fullDirPath, fs, throwOnReadError } = params
const { fullDirPath, fs, throwOnReadError, directoryEntries: entriesParam } = params

const ig = ignore.default()
const directoryEntries = throwOnReadError
? new Set(await fs.readdir(fullDirPath))
: undefined
let directoryEntries = entriesParam
if (!directoryEntries && throwOnReadError) {
directoryEntries = new Set(await fs.readdir(fullDirPath))
}

for (const fileName of PROJECT_IGNORE_FILES) {
const ignoreFilePath = path.join(fullDirPath, fileName)
Expand Down Expand Up @@ -237,6 +253,7 @@ async function parseGitignoreWithMode(params: {
export async function parseGitignore(params: {
fullDirPath: string
fs: CodebuffFileSystem
directoryEntries?: Set<string>
}): Promise<ignore.Ignore> {
return parseGitignoreWithMode({ ...params, throwOnReadError: false })
}
Expand Down
Loading