1import { getExpoHomeDirectory } from '@expo/config/build/getUserState';
2import path from 'path';
3
4import { getReleasedVersionsAsync, 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 versions = await getReleasedVersionsAsync();
62    const version = versions[sdkVersion];
63    debug(`Installing Expo Go version for SDK ${sdkVersion} at URL: ${version[versionsKey]}`);
64    url = version[versionsKey] as string;
65  }
66
67  const filename = path.parse(url).name;
68
69  try {
70    const outputPath = getFilePath(filename);
71    debug(`Downloading Expo Go from "${url}" to "${outputPath}".`);
72    debug(
73      `The requested copy of Expo Go might already be cached in: "${getExpoHomeDirectory()}". You can disable the cache with EXPO_NO_CACHE=1`
74    );
75    await profile(downloadAppAsync)({
76      url,
77      // Save all encrypted cache data to `~/.expo/expo-go`
78      cacheDirectory: 'expo-go',
79      outputPath,
80      extract: shouldExtractResults,
81      onProgress({ progress }) {
82        if (bar) {
83          bar.tick(1, progress);
84        }
85      },
86    });
87    return outputPath;
88  } finally {
89    bar?.terminate();
90  }
91}
92