18d307f52SEvan Baconimport { execFileSync, execSync, ExecSyncOptionsWithStringEncoding } from 'child_process'; 28d307f52SEvan Baconimport * as path from 'path'; 38d307f52SEvan Bacon 4*474a7a4bSEvan Baconconst debug = require('debug')('expo:utils:getRunningProcess') as typeof console.log; 5*474a7a4bSEvan Bacon 68d307f52SEvan Baconconst defaultOptions: ExecSyncOptionsWithStringEncoding = { 78d307f52SEvan Bacon encoding: 'utf8', 88d307f52SEvan Bacon stdio: ['pipe', 'pipe', 'ignore'], 98d307f52SEvan Bacon}; 108d307f52SEvan Bacon 118d307f52SEvan Bacon/** Returns a pid value for a running port like `63828` or null if nothing is running on the given port. */ 128d307f52SEvan Baconexport function getPID(port: number): number | null { 138d307f52SEvan Bacon try { 148d307f52SEvan Bacon const results = execFileSync('lsof', [`-i:${port}`, '-P', '-t', '-sTCP:LISTEN'], defaultOptions) 158d307f52SEvan Bacon .split('\n')[0] 168d307f52SEvan Bacon .trim(); 17*474a7a4bSEvan Bacon const pid = Number(results); 18*474a7a4bSEvan Bacon debug(`pid: ${pid} for port: ${port}`); 19*474a7a4bSEvan Bacon return pid; 20*474a7a4bSEvan Bacon } catch (error: any) { 21*474a7a4bSEvan Bacon debug(`No pid found for port: ${port}. Error: ${error}`); 228d307f52SEvan Bacon return null; 238d307f52SEvan Bacon } 248d307f52SEvan Bacon} 258d307f52SEvan Bacon 268d307f52SEvan Bacon/** Get `package.json` `name` field for a given directory. Returns `null` if none exist. */ 278d307f52SEvan Baconfunction getPackageName(packageRoot: string): string | null { 288d307f52SEvan Bacon const packageJson = path.join(packageRoot, 'package.json'); 298d307f52SEvan Bacon try { 308d307f52SEvan Bacon return require(packageJson).name || null; 318d307f52SEvan Bacon } catch { 328d307f52SEvan Bacon return null; 338d307f52SEvan Bacon } 348d307f52SEvan Bacon} 358d307f52SEvan Bacon 368d307f52SEvan Bacon/** Returns a command like `node /Users/evanbacon/.../bin/expo start` or the package.json name. */ 378d307f52SEvan Baconfunction getProcessCommand(pid: number, procDirectory: string): string { 388d307f52SEvan Bacon const name = getPackageName(procDirectory); 398d307f52SEvan Bacon 408d307f52SEvan Bacon if (name) { 418d307f52SEvan Bacon return name; 428d307f52SEvan Bacon } 438d307f52SEvan Bacon return execSync(`ps -o command -p ${pid} | sed -n 2p`, defaultOptions).replace(/\n$/, '').trim(); 448d307f52SEvan Bacon} 458d307f52SEvan Bacon 468d307f52SEvan Bacon/** Get directory for a given process ID. */ 478d307f52SEvan Baconexport function getDirectoryOfProcessById(processId: number): string { 488d307f52SEvan Bacon return execSync( 498d307f52SEvan Bacon `lsof -p ${processId} | awk '$4=="cwd" {for (i=9; i<=NF; i++) printf "%s ", $i}'`, 508d307f52SEvan Bacon defaultOptions 518d307f52SEvan Bacon ).trim(); 528d307f52SEvan Bacon} 538d307f52SEvan Bacon 548d307f52SEvan Bacon/** Get information about a running process given a port. Returns null if no process is running on the given port. */ 558d307f52SEvan Baconexport function getRunningProcess(port: number): { 568d307f52SEvan Bacon /** The PID value for the port. */ 578d307f52SEvan Bacon pid: number; 588d307f52SEvan Bacon /** Get the directory for the running process. */ 598d307f52SEvan Bacon directory: string; 608d307f52SEvan Bacon /** The command running the process like `node /Users/evanbacon/.../bin/expo start` or the `package.json` name like `my-app`. */ 618d307f52SEvan Bacon command: string; 628d307f52SEvan Bacon} | null { 638d307f52SEvan Bacon // 63828 648d307f52SEvan Bacon const pid = getPID(port); 658d307f52SEvan Bacon if (!pid) { 668d307f52SEvan Bacon return null; 678d307f52SEvan Bacon } 688d307f52SEvan Bacon 698d307f52SEvan Bacon try { 708d307f52SEvan Bacon // /Users/evanbacon/Documents/GitHub/lab/myapp 718d307f52SEvan Bacon const directory = getDirectoryOfProcessById(pid); 728d307f52SEvan Bacon // /Users/evanbacon/Documents/GitHub/lab/myapp/package.json 738d307f52SEvan Bacon const command = getProcessCommand(pid, directory); 748d307f52SEvan Bacon // TODO: Have a better message for reusing another process. 758d307f52SEvan Bacon return { pid, directory, command }; 768d307f52SEvan Bacon } catch { 778d307f52SEvan Bacon return null; 788d307f52SEvan Bacon } 798d307f52SEvan Bacon} 80