import * as fs from 'fs-extra' ; import * as path from 'path' ; async function shouldContinueSearch ( traversedPath , rootPath ? , stopAtPackageJSON ? ) { if ( rootPath ) { return Promise . resolve ( traversedPath !== path . dirname ( rootPath ) ) ; } else if ( stopAtPackageJSON ) { return fs . pathExists ( path . join ( traversedPath , 'package.json' ) ) ; } else { return true ; } } type PathGeneratorFunction = ( traversedPath : string ) => string ; async function traverseAncestorDirectories ( cwd , pathGenerator , rootPath ? , maxItems ? , stopAtPackageJSON ? ) { const paths = [ ] ; let traversedPath = path . resolve ( cwd ) ; while ( await shouldContinueSearch ( traversedPath , rootPath , stopAtPackageJSON ) ) { const generatedPath = pathGenerator ( traversedPath ) ; if ( await fs . pathExists ( generatedPath ) ) { paths . push ( generatedPath ) ; } const parentPath = path . dirname ( traversedPath ) ; if ( parentPath === traversedPath || ( maxItems && paths . length >= maxItems ) ) { break ; } traversedPath = parentPath ; } return paths ; } param cwd param moduleName param rootPath export async function searchForModule ( cwd , moduleName , rootPath ? ) { const pathGenerator = ( traversedPath ) => path . join ( traversedPath , 'node_modules' , moduleName ) ; return traverseAncestorDirectories ( cwd , pathGenerator , rootPath , undefined , true ) ; } param cwd param rootPath export async function searchForNodeModules ( cwd , rootPath ? ) { const pathGenerator = ( traversedPath ) => path . join ( traversedPath , 'node_modules' ) ; return traverseAncestorDirectories ( cwd , pathGenerator , rootPath , undefined , true ) ; } param cwd export async function getProjectRootPath ( cwd ) { for ( const lockFilename of [ 'yarn.lock' , 'package-lock.json' ] ) { const pathGenerator = ( traversedPath ) => path . join ( traversedPath , lockFilename ) ; const lockPaths = await traverseAncestorDirectories ( cwd , pathGenerator , undefined , 1 ) ; if ( lockPaths . length > 0 ) { return path . dirname ( lockPaths [ 0 ] ) ; } } return cwd ; }