1import { getExpoHomeDirectory } from '@expo/config/build/getUserState';
2import path from 'path';
3import ProgressBar from 'progress';
4
5import { getReleasedVersionsAsync, SDKVersion } from '../api/getVersions';
6import * as Log from '../log';
7import { downloadAppAsync } from './downloadAppAsync';
8import { CommandError } from './errors';
9import { profile } from './profile';
10import { setProgressBar } from './progress';
11
12const platformSettings: Record<
13  string,
14  {
15    shouldExtractResults: boolean;
16    versionsKey: keyof SDKVersion;
17    getFilePath: (filename: string) => string;
18  }
19> = {
20  ios: {
21    versionsKey: 'iosClientUrl',
22    getFilePath: (filename) =>
23      path.join(getExpoHomeDirectory(), 'ios-simulator-app-cache', `${filename}.app`),
24    shouldExtractResults: true,
25  },
26  android: {
27    versionsKey: 'androidClientUrl',
28    getFilePath: (filename) =>
29      path.join(getExpoHomeDirectory(), 'android-apk-cache', `${filename}.apk`),
30    shouldExtractResults: false,
31  },
32};
33
34/** Download the Expo Go app from the Expo servers (if only it was this easy for every app). */
35export async function downloadExpoGoAsync(
36  platform: keyof typeof platformSettings,
37  {
38    url,
39    sdkVersion,
40  }: {
41    url?: string;
42    sdkVersion?: string;
43  }
44): Promise<string> {
45  const { getFilePath, versionsKey, shouldExtractResults } = platformSettings[platform];
46
47  const bar = new ProgressBar('Downloading the Expo Go app [:bar] :percent :etas', {
48    width: 64,
49    total: 100,
50    clear: true,
51    complete: '=',
52    incomplete: ' ',
53  });
54  // TODO: Auto track progress
55  setProgressBar(bar);
56
57  if (!url) {
58    if (!sdkVersion) {
59      throw new CommandError(
60        `Unable to determine which Expo Go version to install (platform: ${platform})`
61      );
62    }
63    const versions = await getReleasedVersionsAsync();
64    const version = versions[sdkVersion];
65    Log.debug(`Installing Expo Go version for SDK ${sdkVersion} at URL: ${version[versionsKey]}`);
66    url = version[versionsKey] as string;
67  }
68
69  const filename = path.parse(url).name;
70
71  try {
72    const outputPath = getFilePath(filename);
73    Log.debug(`Downloading Expo Go from "${url}" to "${outputPath}".`);
74    Log.debug(
75      `The requested copy of Expo Go might already be cached in: "${getExpoHomeDirectory()}". You can disable the cache with EXPO_NO_CACHE=1`
76    );
77    await profile(downloadAppAsync)({
78      url,
79      // Save all encrypted cache data to `~/.expo/expo-go`
80      cacheDirectory: 'expo-go',
81      outputPath,
82      extract: shouldExtractResults,
83      onProgress({ progress }) {
84        if (bar) {
85          bar.tick(1, progress);
86        }
87      },
88    });
89    return outputPath;
90  } finally {
91    bar.terminate();
92    setProgressBar(null);
93  }
94}
95