Skip to content

Commit 324dc9e

Browse files
committed
Eliminate redundant stat calls and queue shifts in getProjectFileTree
1 parent 59cca53 commit 324dc9e

2 files changed

Lines changed: 52 additions & 13 deletions

File tree

common/src/__tests__/project-file-tree.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,28 @@ describe('getProjectFileTree', () => {
8888
expect(filePaths).toEqual(['app/src/main/Inventory.kt'])
8989
})
9090

91+
it('does not stat ignore files when none exist in directory entries', async () => {
92+
const root = '/repo'
93+
const fs = createFsWithFiles(root, ['src/index.ts', 'src/util.ts'])
94+
const statCalls: string[] = []
95+
const originalStat = fs.stat
96+
;(fs as any).stat = async (p: any, ...args: any[]) => {
97+
statCalls.push(String(p))
98+
return (originalStat as any)(p, ...args)
99+
}
100+
101+
await getProjectFileTree({ projectRoot: root, fs })
102+
103+
// None of the stat calls should be for ignore files
104+
const ignoreStatCalls = statCalls.filter(
105+
(p) =>
106+
p.endsWith('.gitignore') ||
107+
p.endsWith('.codebuffignore') ||
108+
p.endsWith('.manicodeignore'),
109+
)
110+
expect(ignoreStatCalls).toHaveLength(0)
111+
})
112+
91113
it('prunes directories ignored by a rule in a nested .gitignore', async () => {
92114
const root = '/repo'
93115
const fs = createFsWithFiles(root, [

common/src/project-file-tree.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -128,20 +128,34 @@ export async function getProjectFileTree(params: {
128128
]
129129
let totalFiles = 0
130130
let dirsScanned = 0
131+
let queueIndex = 0
131132

132-
while (queue.length > 0 && totalFiles < maxFiles && dirsScanned < maxDirs) {
133-
const { node, fullPath, ignores, depth } = queue.shift()!
133+
while (queueIndex < queue.length && totalFiles < maxFiles && dirsScanned < maxDirs) {
134+
const { node, fullPath, ignores, depth } = queue[queueIndex++]
134135
dirsScanned++
135-
const dirIgnores = [
136-
...ignores,
137-
{
138-
base: toPosixPath(path.relative(projectRoot, fullPath)),
139-
ig: await parseGitignore({ fullDirPath: fullPath, fs }),
140-
},
141-
]
142136

143137
try {
144138
const files = await fs.readdir(fullPath)
139+
const filesSet = new Set(files)
140+
141+
let dirIgnores = ignores
142+
const hasIgnoreFile = PROJECT_IGNORE_FILES.some((fileName) =>
143+
filesSet.has(fileName),
144+
)
145+
if (hasIgnoreFile) {
146+
dirIgnores = [
147+
...ignores,
148+
{
149+
base: toPosixPath(path.relative(projectRoot, fullPath)),
150+
ig: await parseGitignore({
151+
fullDirPath: fullPath,
152+
fs,
153+
directoryEntries: filesSet,
154+
}),
155+
},
156+
]
157+
}
158+
145159
for (const file of files) {
146160
if (totalFiles >= maxFiles) break
147161

@@ -199,13 +213,15 @@ async function parseGitignoreWithMode(params: {
199213
fullDirPath: string
200214
fs: CodebuffFileSystem
201215
throwOnReadError: boolean
216+
directoryEntries?: Set<string>
202217
}): Promise<ignore.Ignore> {
203-
const { fullDirPath, fs, throwOnReadError } = params
218+
const { fullDirPath, fs, throwOnReadError, directoryEntries: entriesParam } = params
204219

205220
const ig = ignore.default()
206-
const directoryEntries = throwOnReadError
207-
? new Set(await fs.readdir(fullDirPath))
208-
: undefined
221+
let directoryEntries = entriesParam
222+
if (!directoryEntries && throwOnReadError) {
223+
directoryEntries = new Set(await fs.readdir(fullDirPath))
224+
}
209225

210226
for (const fileName of PROJECT_IGNORE_FILES) {
211227
const ignoreFilePath = path.join(fullDirPath, fileName)
@@ -237,6 +253,7 @@ async function parseGitignoreWithMode(params: {
237253
export async function parseGitignore(params: {
238254
fullDirPath: string
239255
fs: CodebuffFileSystem
256+
directoryEntries?: Set<string>
240257
}): Promise<ignore.Ignore> {
241258
return parseGitignoreWithMode({ ...params, throwOnReadError: false })
242259
}

0 commit comments

Comments
 (0)