/** * 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 hasConnectedDevice() { const physicalDevices = exec('adb devices | grep -v emulator', {silent: true}) .stdout.trim() .split('\n') .slice(1); return physicalDevices.length > 0; } function maybeLaunchAndroidEmulator() { if (hasConnectedDevice) { console.info('Already have a device connected. Skip launching emulator.'); return; } 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 function launchPackagerInSeparateWindow(folderPath) { const command = `tell application "Terminal" to do script "cd ${folderPath} && yarn start"`; exec(`osascript -e '${command}'`); } module.exports = { maybeLaunchAndroidEmulator, isPackagerRunning, launchPackagerInSeparateWindow, };