1import * as PackageManager from '@expo/package-manager'; 2import { execSync } from 'child_process'; 3 4import { CLI_NAME } from './cmd'; 5 6export type PackageManagerName = 'npm' | 'pnpm' | 'yarn' | 'bun'; 7 8const debug = require('debug')('expo:init:resolvePackageManager') as typeof console.log; 9 10/** Determine which package manager to use for installing dependencies based on how the process was started. */ 11export function resolvePackageManager(): PackageManagerName { 12 // Attempt to detect if the user started the command using `yarn` or `pnpm` or `bun` 13 const userAgent = process.env.npm_config_user_agent; 14 debug('npm_config_user_agent:', userAgent); 15 if (userAgent?.startsWith('yarn')) { 16 return 'yarn'; 17 } else if (userAgent?.startsWith('pnpm')) { 18 return 'pnpm'; 19 } else if (userAgent?.startsWith('bun')) { 20 return 'bun'; 21 } else if (userAgent?.startsWith('npm')) { 22 return 'npm'; 23 } 24 25 // Try availability 26 if (isPackageManagerAvailable('yarn')) { 27 return 'yarn'; 28 } else if (isPackageManagerAvailable('pnpm')) { 29 return 'pnpm'; 30 } else if (isPackageManagerAvailable('bun')) { 31 return 'bun'; 32 } 33 34 return 'npm'; 35} 36 37export function isPackageManagerAvailable(manager: PackageManagerName): boolean { 38 try { 39 execSync(`${manager} --version`, { stdio: 'ignore' }); 40 return true; 41 } catch {} 42 return false; 43} 44 45export function formatRunCommand(packageManager: PackageManagerName, cmd: string) { 46 switch (packageManager) { 47 case 'pnpm': 48 return `pnpm run ${cmd}`; 49 case 'yarn': 50 return `yarn ${cmd}`; 51 case 'bun': 52 return `bun run ${cmd}`; 53 case 'npm': 54 default: 55 return `npm run ${cmd}`; 56 } 57} 58 59export function formatSelfCommand() { 60 const packageManager = resolvePackageManager(); 61 switch (packageManager) { 62 case 'pnpm': 63 return `pnpx ${CLI_NAME}`; 64 case 'bun': 65 return `bunx ${CLI_NAME}`; 66 case 'yarn': 67 case 'npm': 68 default: 69 return `npx ${CLI_NAME}`; 70 } 71} 72 73export async function installDependenciesAsync( 74 projectRoot: string, 75 packageManager: PackageManagerName, 76 flags: { silent: boolean } = { silent: false } 77) { 78 const options = { cwd: projectRoot, silent: flags.silent }; 79 if (packageManager === 'yarn') { 80 await new PackageManager.YarnPackageManager(options).installAsync(); 81 } else if (packageManager === 'pnpm') { 82 await new PackageManager.PnpmPackageManager(options).installAsync(); 83 } else if (packageManager === 'bun') { 84 await new PackageManager.BunPackageManager(options).installAsync(); 85 } else { 86 await new PackageManager.NpmPackageManager(options).installAsync(); 87 } 88} 89