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