11import  *  as  path  from  "path" 
22import  *  as  fs  from  "fs" 
33import  *  as  which  from  "which" 
4+ import  {  execSync  }  from  "child_process" 
45
56export  enum  PM  { 
67  NPM  =  "npm" , 
78  YARN  =  "yarn" , 
89  PNPM  =  "pnpm" , 
910  YARN_BERRY  =  "yarn-berry" , 
11+   BUN  =  "bun" , 
1012} 
1113
1214// Cache for resolved paths 
@@ -15,6 +17,7 @@ const pmPathCache: Record<PM, string | null | undefined> = {
1517  [ PM . YARN ] : undefined , 
1618  [ PM . PNPM ] : undefined , 
1719  [ PM . YARN_BERRY ] : undefined , 
20+   [ PM . BUN ] : undefined , 
1821} 
1922
2023function  resolveCommand ( pm : PM ) : string  { 
@@ -42,46 +45,54 @@ export function getPackageManagerCommand(pm: PM) {
4245  return  resolved 
4346} 
4447
45- export  function  detectPackageManagerByEnv ( pm :  "npm"   |   "yarn"   |   "pnpm" ) : PM  |  null  { 
46-   const  ua  =  process . env . npm_config_user_agent   ??   "" 
47-   const  execPath  =  process . env . npm_execpath ?. toLowerCase ( )  ??    "" 
48+ export  function  detectPackageManagerByEnv ( ) : PM  |  null  { 
49+   const  packageJsonPath  =  path . join ( process . cwd ( ) ,   "package.json" ) 
50+   const  packageManager  =  fs . existsSync ( packageJsonPath )  ?  JSON . parse ( fs . readFileSync ( packageJsonPath ,   "utf8" ) ) ?. packageManager  :  undefined 
4851
49-   const  yarnVersion  =  process . env . YARN_VERSION 
50-   const  isBerry  =  yarnVersion ?. startsWith ( "2." )  ||  yarnVersion ?. startsWith ( "3." ) 
52+   const  priorityChecklist  =  [ 
53+     ( key : string )  =>  process . env . npm_config_user_agent ?. includes ( key ) , 
54+     ( key : string )  =>  process . env . npm_execpath ?. includes ( key ) , 
55+     ( key : string )  =>  packageManager ?. startsWith ( `${ key }  ) , 
56+   ] 
5157
52-   switch  ( pm )  { 
53-     case  "pnpm" :
54-       return  ua . includes ( "pnpm" )  ||  execPath . includes ( "pnpm" )  ||  process . env . PNPM_HOME  ? PM . PNPM  : null 
55-     case  "yarn" :
56-       if  ( ua . includes ( "yarn" )  ||  execPath . includes ( "yarn" )  ||  process . env . YARN_REGISTRY )  { 
57-         return  isBerry  ||  ua . includes ( "yarn/2" )  ||  ua . includes ( "yarn/3" )  ? PM . YARN_BERRY  : PM . YARN 
58+   const  pms  =  Object . values ( PM ) . filter ( pm  =>  pm  !==  PM . YARN_BERRY ) 
59+   for  ( const  checker  of  priorityChecklist )  { 
60+     for  ( const  pm  of  pms )  { 
61+       if  ( checker ( pm ) )  { 
62+         return  pm 
5863      } 
59-       return  null 
60-     case  "npm" :
61-       return  ua . includes ( "npm" )  ||  execPath . includes ( "npm" )  ||  process . env . npm_package_json  ? PM . NPM  : null 
62-     default :
63-       return  null 
64+     } 
6465  } 
66+   return  null 
6567} 
6668
6769export  function  detectPackageManagerByLockfile ( cwd : string ) : PM  |  null  { 
6870  const  has  =  ( file : string )  =>  fs . existsSync ( path . join ( cwd ,  file ) ) 
6971
70-   const  yarn  =  has ( "yarn.lock" ) 
71-   const  pnpm  =  has ( "pnpm-lock.yaml" ) 
72-   const  npm  =  has ( "package-lock.json" ) 
73- 
7472  const  detected : PM [ ]  =  [ ] 
75-   if  ( yarn )  detected . push ( PM . YARN ) 
76-   if  ( pnpm )  detected . push ( PM . PNPM ) 
77-   if  ( npm )  detected . push ( PM . NPM ) 
73+   if  ( has ( "yarn.lock" ) )  { 
74+     detected . push ( PM . YARN ) 
75+   } 
76+   if  ( has ( "pnpm-lock.yaml" ) )  { 
77+     detected . push ( PM . PNPM ) 
78+   } 
79+   if  ( has ( "package-lock.json" ) )  { 
80+     detected . push ( PM . NPM ) 
81+   } 
82+   if  ( has ( "bun.lock" )  ||  has ( "bun.lockb" ) )  { 
83+     detected . push ( PM . BUN ) 
84+   } 
7885
7986  if  ( detected . length  ===  1 )  { 
80-     if  ( detected [ 0 ]  ===  PM . YARN )  { 
81-       return  detectPackageManagerByEnv ( "yarn" )  ===  PM . YARN_BERRY  ? PM . YARN_BERRY  : PM . YARN 
82-     } 
8387    return  detected [ 0 ] 
8488  } 
8589
8690  return  null 
8791} 
92+ 
93+ export  function  detectYarnBerry ( )  { 
94+   // yarn --version 
95+   const  version  =  execSync ( "yarn --version" ) . toString ( ) . trim ( ) 
96+   if  ( parseInt ( version . split ( "." ) [ 0 ] )  >  1 )  return  PM . YARN_BERRY 
97+   return  PM . YARN 
98+ } 
0 commit comments