import spawnAsync, { SpawnOptions, SpawnPromise } from '@expo/spawn-async'; import sudo from 'sudo-prompt'; /** * The pending spawn promise is similar to the spawn promise from `@expo/spawn-async`. * Instead of the `child` process being available immediately, the `child` is behind another promise. * We need this to perform async tasks before running the actual spawn promise. * Use it like: `await manager.installAsync().child` */ export interface PendingSpawnPromise extends Promise { /** * The child process from the delayed spawn. * This is `null` whenever the promise before the spawn promise is rejected. */ child: Promise['child'] | null>; } export function createPendingSpawnAsync( actionAsync: () => Promise, spawnAsync: (result: V) => SpawnPromise ): PendingSpawnPromise { // Manually rsolve the child promise whenever the prepending async action is resolved. // Avoid `childReject` to prevent "unhandled promise rejection" for one of the two promises. let childResolve: (child: SpawnPromise['child'] | null) => void; const child: Promise['child'] | null> = new Promise((resolve, reject) => { childResolve = resolve; }); const pendingPromise = new Promise((spawnResolve, spawnReject) => { actionAsync() .then((result) => { const spawnPromise = spawnAsync(result); childResolve(spawnPromise.child); spawnPromise.then(spawnResolve).catch(spawnReject); }) .catch((error) => { childResolve(null); spawnReject(error); }); }); (pendingPromise as PendingSpawnPromise).child = child; return pendingPromise as PendingSpawnPromise; } /** * Spawn a command with sudo privileges. * On windows, this uses the `sudo-prompt` package. * on other systems, this uses the `sudo` binary. */ export async function spawnSudoAsync(command: string[], spawnOptions: SpawnOptions): Promise { // sudo prompt only seems to work on win32 machines. if (process.platform === 'win32') { return new Promise((resolve, reject) => { sudo.exec(command.join(' '), { name: 'pod install' }, (error) => { if (error) { reject(error); } resolve(); }); }); } else { // Attempt to use sudo to run the command on Mac and Linux. // TODO(Bacon): Make a v of sudo-prompt that's win32 only for better bundle size. console.log( 'Your password might be needed to install CocoaPods CLI: https://guides.cocoapods.org/using/getting-started.html#installation' ); await spawnAsync('sudo', command, spawnOptions); } }