@@ -241,68 +241,96 @@ export function updateGitignore(
241
241
}
242
242
}
243
243
244
- export function findFileInRepo (
245
- targetFilename : string ,
246
- startDir : string = process . cwd ( ) ,
247
- maxDepth : number = 5 /* depth=5 should be good enough to handle most repos? */ ,
248
- skipDirs : string [ ] = [ "node_modules" , ".cache" ] ,
249
- ) : string | null {
250
- let currentDir = startDir ;
251
- let depth = 0 ;
252
-
253
- while ( depth < maxDepth ) {
254
- const filePath = findFileInDirectory (
255
- currentDir ,
256
- targetFilename . toLowerCase ( ) ,
257
- skipDirs ,
244
+ export function findClosestFile ( {
245
+ fileName,
246
+ maxDepth = 5 ,
247
+ maxLevelsUp = 5 ,
248
+ stopMarkers = [ ".git" , "package.json" , "cargo.toml" ] ,
249
+ startDir = process . cwd ( ) ,
250
+ skipDirs = [ "node_modules" ] ,
251
+ } : {
252
+ fileName : string ;
253
+ maxDepth ?: number ;
254
+ maxLevelsUp ?: number ;
255
+ stopMarkers ?: string [ ] ;
256
+ skipDirs ?: string [ ] ;
257
+ startDir ?: string ;
258
+ } ) : string | null {
259
+ const visited = new Set < string > ( ) ;
260
+
261
+ // check if we're already in what looks like a repo
262
+ const isLikelyRepo = stopMarkers . some ( ( marker ) =>
263
+ fs . existsSync ( path . join ( startDir , marker ) ) ,
264
+ ) ;
265
+
266
+ // if we are not in what looks like a repo, limit the search more aggressively
267
+ if ( ! isLikelyRepo ) {
268
+ maxDepth = 2 ;
269
+ maxLevelsUp = 2 ;
270
+ }
271
+
272
+ function crawlUp ( currentDir : string , levelsUp : number ) : string | null {
273
+ if ( levelsUp > maxLevelsUp ) return null ;
274
+
275
+ let targetPath = path . join ( currentDir , fileName ) ;
276
+ if ( fs . existsSync ( targetPath ) ) return targetPath ;
277
+ targetPath = path . join ( currentDir , fileName . toLowerCase ( ) ) ;
278
+ if ( fs . existsSync ( targetPath ) ) return targetPath ;
279
+
280
+ const hasMarker = stopMarkers . some ( ( marker ) =>
281
+ fs . existsSync ( path . join ( currentDir , marker ) ) ,
258
282
) ;
259
- if ( filePath ) return filePath ;
260
283
261
- // stop searching once we hit the repository root
262
- const gitDir = path . join ( currentDir , ".git" ) ;
263
- if ( fs . existsSync ( gitDir ) && fs . statSync ( gitDir ) . isDirectory ( ) ) {
264
- return null ;
265
- }
284
+ if ( hasMarker && levelsUp > 0 ) return null ;
266
285
267
- // Move up one directory
268
286
const parentDir = path . dirname ( currentDir ) ;
269
- if ( parentDir === currentDir ) {
270
- return null ;
271
- }
287
+ if ( parentDir === currentDir ) return null ;
272
288
273
- currentDir = parentDir ;
274
- depth ++ ;
289
+ return crawlUp ( parentDir , levelsUp + 1 ) ;
275
290
}
276
291
277
- return null ;
278
- }
292
+ function crawlDown ( dir : string , depth : number ) : string | null {
293
+ if ( depth > maxDepth ) return null ;
294
+ if ( visited . has ( dir ) ) return null ;
295
+ visited . add ( dir ) ;
279
296
280
- export function findFileInDirectory (
281
- dir : string ,
282
- targetFilename : string ,
283
- skipDirs : string [ ] ,
284
- ) : string | null {
285
- const files = fs . readdirSync ( dir ) ;
286
-
287
- for ( const file of files ) {
288
- const absolutePath = path . join ( dir , file ) ;
289
297
try {
290
- if ( fs . statSync ( absolutePath ) . isDirectory ( ) ) {
291
- if ( skipDirs . includes ( file ) ) {
298
+ let targetPath = path . join ( dir , fileName ) ;
299
+ if ( fs . existsSync ( targetPath ) ) return targetPath ;
300
+ targetPath = path . join ( dir , fileName . toLowerCase ( ) ) ;
301
+ if ( fs . existsSync ( targetPath ) ) return targetPath ;
302
+
303
+ const entries = fs . readdirSync ( dir , { withFileTypes : true } ) ;
304
+
305
+ for ( const entry of entries ) {
306
+ // skip hidden directories and node_modules
307
+ if ( entry . name . startsWith ( "." ) || skipDirs . includes ( entry . name ) )
292
308
continue ;
309
+
310
+ if ( entry . isDirectory ( ) ) {
311
+ const fullPath = path . join ( dir , entry . name ) ;
312
+
313
+ const hasMarker = stopMarkers . some ( ( marker ) =>
314
+ fs . existsSync ( path . join ( fullPath , marker ) ) ,
315
+ ) ;
316
+ if ( hasMarker ) continue ;
317
+
318
+ const result = crawlDown ( fullPath , depth + 1 ) ;
319
+ if ( result ) return result ;
293
320
}
294
- const result = findFileInDirectory (
295
- absolutePath ,
296
- targetFilename ,
297
- skipDirs ,
298
- ) ;
299
- if ( result ) return result ; // Found in a subdirectory
300
- } else if ( file . toLowerCase ( ) === targetFilename ) {
301
- return absolutePath ;
302
321
}
303
- } catch ( err ) { }
322
+ } catch ( error ) {
323
+ // honey badger don't care about errors
324
+ return null ;
325
+ }
326
+
327
+ return null ;
304
328
}
305
329
330
+ const upResult = crawlUp ( startDir , 0 ) ;
331
+ if ( upResult ) return upResult ;
332
+ if ( isLikelyRepo ) return crawlDown ( startDir , 0 ) ;
333
+
306
334
return null ;
307
335
}
308
336
0 commit comments