import fs from 'fs' ; import path from 'path' ; import { promisify } from 'util' ; const readdirAsync = promisify ( fs . readdir ) ; const realpathAsync = promisify ( fs . realpath ) ; const statAsync = promisify ( fs . stat ) ; const readdirSync = fs . readdirSync ; const realpathSync = fs . realpathSync ; const statSync = fs . statSync ; type ItemType = 'd' | 'f' | 'l' ; type Handler = ( base : string , item : string , type : ItemType ) => boolean | string ; param dirent returns function direntToType ( dirent ) { return dirent . isFile ( ) ? 'f' : dirent . isDirectory ( ) ? 'd' : dirent . isSymbolicLink ( ) ? 'l' : undefined ; } param string param regexs returns function isMatched ( string , regexs ) { for ( const regex of regexs ) { if ( typeof regex === 'string' ) { if ( string . includes ( regex ) ) { return true ; } } else if ( regex . exec ( string ) ) { return true ; } } return false ; } param root param match param types param deep param limit param handler param found param seen returns export function findSync ( root , match , types = [ 'f' , 'd' , 'l' ] , deep = [ ] , limit = Infinity , handler = ( ) => true , found = [ ] , seen = { } ) { try { const realRoot = realpathSync ( root ) ; if ( seen [ realRoot ] ) { return found ; } if ( limit - found . length <= 0 ) { return found ; } if ( direntToType ( statSync ( realRoot ) ) !== 'd' ) { return found ; } const items = readdirSync ( root , { withFileTypes : true } ) ; seen [ realRoot ] = true ; for ( const item of items ) { const itemName = item . name ; const itemType = direntToType ( item ) ; const itemPath = path . join ( root , item . name ) ; if ( itemType && types . includes ( itemType ) ) { if ( isMatched ( itemPath , match ) ) { const value = handler ( root , itemName , itemType ) ; if ( typeof value === 'string' ) { found . push ( value ) ; } else if ( value === true ) { found . push ( itemPath ) ; } } } if ( deep . includes ( itemType as any ) ) { findSync ( itemPath , match , types , deep , limit , handler , found , seen ) ; } } } catch { } return found ; } param root param match param types param deep param limit param filter param found param seen returns export function findUpSync ( root , match , types = [ 'f' , 'd' , 'l' ] , deep = [ ] , limit = Infinity , handler = ( ) => true , found = [ ] , seen = { } ) { if ( path . resolve ( root ) === path . resolve ( root , '..' ) ) { return found ; } findSync ( root , match , types , deep , limit , handler , found , seen ) ; if ( found . length === 0 ) { const parent = path . join ( root , '..' ) ; findUpSync ( parent , match , types , deep , limit , handler , found , seen ) ; } return found ; } param root param match param types param deep param limit param filter param found param seen returns export async function findAsync ( root , match , types = [ 'f' , 'd' , 'l' ] , deep = [ ] , limit = Infinity , handler = ( ) => true , found = [ ] , seen = { } ) { try { const realRoot = await realpathAsync ( root ) ; if ( seen [ realRoot ] ) { return found ; } if ( limit - found . length <= 0 ) { return found ; } if ( direntToType ( await statAsync ( realRoot ) ) !== 'd' ) { return found ; } const items = await readdirAsync ( root , { withFileTypes : true } ) ; seen [ realRoot ] = true ; for ( const item of items ) { const itemName = item . name ; const itemType = direntToType ( item ) ; const itemPath = path . join ( root , item . name ) ; if ( itemType && types . includes ( itemType ) ) { if ( isMatched ( itemPath , match ) ) { const value = handler ( root , itemName , itemType ) ; if ( typeof value === 'string' ) { found . push ( value ) ; } else if ( value === true ) { found . push ( itemPath ) ; } } } if ( deep . includes ( itemType as any ) ) { await findAsync ( itemPath , match , types , deep , limit , handler , found , seen ) ; } } } catch { } return found ; } param root param match param types param deep param limit param filter param found param seen returns export async function findUpAsync ( root , match , types = [ 'f' , 'd' , 'l' ] , deep = [ ] , limit = Infinity , handler = ( ) => true , found = [ ] , seen = { } ) { if ( path . resolve ( root ) === path . resolve ( root , '..' ) ) { return found ; } await findAsync ( root , match , types , deep , limit , handler , found , seen ) ; if ( found . length === 0 ) { const parent = path . join ( root , '..' ) ; await findUpAsync ( parent , match , types , deep , limit , handler , found , seen ) ; } return found ; }