1import { getExpoHomeDirectory } from '@expo/config/build/getUserState';
2import path from 'path';
3
4import { getVersionsAsync, SDKVersion } from '../api/getVersions';
5import { downloadAppAsync } from './downloadAppAsync';
6import { CommandError } from './errors';
7import { profile } from './profile';
8import { createProgressBar } from './progress';
9
10const debug = require('debug')('expo:utils:downloadExpoGo') as typeof console.log;
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 = createProgressBar('Downloading the Expo Go app [:bar] :percent :etas', {
48    width: 64,
49    total: 100,
50    clear: true,
51    complete: '=',
52    incomplete: ' ',
53  });
54
55  if (!url) {
56    if (!sdkVersion) {
57      throw new CommandError(
58        `Unable to determine which Expo Go version to install (platform: ${platform})`
59      );
60    }
61    const { sdkVersions: versions } = await getVersionsAsync();
62
63    const version = versions[sdkVersion];
64    if (!version) {
65      throw new CommandError(
66        `Unable to find a version of Expo Go for SDK ${sdkVersion} (platform: ${platform})`
67      );
68    }
69    debug(`Installing Expo Go version for SDK ${sdkVersion} at URL: ${version[versionsKey]}`);
70    url = version[versionsKey] as string;
71  }
72
73  const filename = path.parse(url).name;
74
75  try {
76    const outputPath = getFilePath(filename);
77    debug(`Downloading Expo Go from "${url}" to "${outputPath}".`);
78    debug(
79      `The requested copy of Expo Go might already be cached in: "${getExpoHomeDirectory()}". You can disable the cache with EXPO_NO_CACHE=1`
80    );
81    await profile(downloadAppAsync)({
82      url,
83      // Save all encrypted cache data to `~/.expo/expo-go`
84      cacheDirectory: 'expo-go',
85      outputPath,
86      extract: shouldExtractResults,
87      onProgress({ progress }) {
88        if (bar) {
89          bar.tick(1, progress);
90        }
91      },
92    });
93    return outputPath;
94  } finally {
95    bar?.terminate();
96  }
97}
98