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