1import { execFileSync, execSync, ExecSyncOptionsWithStringEncoding } from 'child_process'; 2import * as path from 'path'; 3 4const defaultOptions: ExecSyncOptionsWithStringEncoding = { 5 encoding: 'utf8', 6 stdio: ['pipe', 'pipe', 'ignore'], 7}; 8 9/** Returns a pid value for a running port like `63828` or null if nothing is running on the given port. */ 10export function getPID(port: number): number | null { 11 try { 12 const results = execFileSync('lsof', [`-i:${port}`, '-P', '-t', '-sTCP:LISTEN'], defaultOptions) 13 .split('\n')[0] 14 .trim(); 15 return Number(results); 16 } catch { 17 return null; 18 } 19} 20 21/** Get `package.json` `name` field for a given directory. Returns `null` if none exist. */ 22function getPackageName(packageRoot: string): string | null { 23 const packageJson = path.join(packageRoot, 'package.json'); 24 try { 25 return require(packageJson).name || null; 26 } catch { 27 return null; 28 } 29} 30 31/** Returns a command like `node /Users/evanbacon/.../bin/expo start` or the package.json name. */ 32function getProcessCommand(pid: number, procDirectory: string): string { 33 const name = getPackageName(procDirectory); 34 35 if (name) { 36 return name; 37 } 38 return execSync(`ps -o command -p ${pid} | sed -n 2p`, defaultOptions).replace(/\n$/, '').trim(); 39} 40 41/** Get directory for a given process ID. */ 42export function getDirectoryOfProcessById(processId: number): string { 43 return execSync( 44 `lsof -p ${processId} | awk '$4=="cwd" {for (i=9; i<=NF; i++) printf "%s ", $i}'`, 45 defaultOptions 46 ).trim(); 47} 48 49/** Get information about a running process given a port. Returns null if no process is running on the given port. */ 50export function getRunningProcess(port: number): { 51 /** The PID value for the port. */ 52 pid: number; 53 /** Get the directory for the running process. */ 54 directory: string; 55 /** The command running the process like `node /Users/evanbacon/.../bin/expo start` or the `package.json` name like `my-app`. */ 56 command: string; 57} | null { 58 // 63828 59 const pid = getPID(port); 60 if (!pid) { 61 return null; 62 } 63 64 try { 65 // /Users/evanbacon/Documents/GitHub/lab/myapp 66 const directory = getDirectoryOfProcessById(pid); 67 // /Users/evanbacon/Documents/GitHub/lab/myapp/package.json 68 const command = getProcessCommand(pid, directory); 69 // TODO: Have a better message for reusing another process. 70 return { pid, directory, command }; 71 } catch { 72 return null; 73 } 74} 75