1import * as osascript from '@expo/osascript'; 2import { spawn, type ChildProcess } from 'child_process'; 3import { sync as globSync } from 'glob'; 4import path from 'path'; 5 6import { 7 LaunchBrowserTypes, 8 type LaunchBrowserImpl, 9 type LaunchBrowserInstance, 10} from './LaunchBrowser.types'; 11 12/** 13 * Browser implementation for macOS 14 */ 15export default class LaunchBrowserImplMacOS implements LaunchBrowserImpl, LaunchBrowserInstance { 16 private _process: ChildProcess | undefined; 17 18 MAP = { 19 [LaunchBrowserTypes.CHROME]: 'google chrome', 20 [LaunchBrowserTypes.EDGE]: 'microsoft edge', 21 }; 22 23 async isSupportedBrowser(browserType: LaunchBrowserTypes): Promise<boolean> { 24 let result = false; 25 try { 26 await osascript.execAsync(`id of application "${this.MAP[browserType]}"`); 27 result = true; 28 } catch { 29 result = false; 30 } 31 return result; 32 } 33 34 async createTempBrowserDir(baseDirName: string) { 35 return path.join(require('temp-dir'), baseDirName); 36 } 37 38 async launchAsync( 39 browserType: LaunchBrowserTypes, 40 args: string[] 41 ): Promise<LaunchBrowserInstance> { 42 const appDirectory = await osascript.execAsync( 43 `POSIX path of (path to application "${this.MAP[browserType]}")` 44 ); 45 const appPath = globSync('Contents/MacOS/*', { cwd: appDirectory.trim(), absolute: true })?.[0]; 46 if (!appPath) { 47 throw new Error(`Cannot find application path from ${appDirectory}Contents/MacOS`); 48 } 49 this._process = spawn(appPath, args, { stdio: 'ignore' }); 50 51 return this; 52 } 53 54 async close(): Promise<void> { 55 this._process?.kill(); 56 this._process = undefined; 57 } 58} 59