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