/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ 'use strict'; const {exec} = require('shelljs'); const os = require('os'); const {spawn} = require('node:child_process'); /* * Android related utils - leverages android tooling */ // this code is taken from the CLI repo, slightly readapted to our needs // here's the reference folder: // https://github.com/react-native-community/cli/blob/main/packages/cli-platform-android/src/commands/runAndroid const emulatorCommand = process.env.ANDROID_HOME ? `${process.env.ANDROID_HOME}/emulator/emulator` : 'emulator'; const getEmulators = () => { const emulatorsOutput = exec(`${emulatorCommand} -list-avds`).stdout; return emulatorsOutput.split(os.EOL).filter(name => name !== ''); }; const launchEmulator = emulatorName => { // we need both options 'cause reasons: // from docs: "When using the detached option to start a long-running process, the process will not stay running in the background after the parent exits unless it is provided with a stdio configuration that is not connected to the parent. If the parent's stdio is inherited, the child will remain attached to the controlling terminal." // here: https://nodejs.org/api/child_process.html#optionsdetached const cp = spawn(emulatorCommand, [`@${emulatorName}`], { detached: true, stdio: 'ignore', }); cp.unref(); }; function tryLaunchEmulator() { const emulators = getEmulators(); if (emulators.length > 0) { try { launchEmulator(emulators[0]); return {success: true}; } catch (error) { return {success: false, error}; } } return { success: false, error: 'No emulators found as an output of `emulator -list-avds`', }; } function launchAndroidEmulator() { const result = tryLaunchEmulator(); if (result.success) { console.info('Successfully launched emulator.'); } else { console.error(`Failed to launch emulator. Reason: ${result.error || ''}.`); console.warn( 'Please launch an emulator manually or connect a device. Otherwise app may fail to launch.', ); } } /* * iOS related utils - leverages xcodebuild */ /* * Metro related utils */ // inspired by CLI again https://github.com/react-native-community/cli/blob/main/packages/cli-tools/src/isPackagerRunning.ts function isPackagerRunning( packagerPort = process.env.RCT_METRO_PORT || '8081', ) { try { const status = exec(`curl http://localhost:${packagerPort}/status`, { silent: true, }).stdout; return status === 'packager-status:running' ? 'running' : 'unrecognized'; } catch (_error) { return 'not_running'; } } // this is a very limited implementation of how this should work // literally, this is macos only // a more robust implementation can be found here: // https://github.com/react-native-community/cli/blob/7c003f2b1d9d80ec5c167614ba533a004272c685/packages/cli-platform-android/src/commands/runAndroid/index.ts#L195 function launchPackagerInSeparateWindow() { exec("open -a 'Terminal' ./scripts/packager.sh"); } module.exports = { launchAndroidEmulator, isPackagerRunning, launchPackagerInSeparateWindow, };