18d307f52SEvan Baconimport { ExpoConfig, getConfig, PackageJSONConfig } from '@expo/config';
28d307f52SEvan Baconimport { ModPlatform } from '@expo/config-plugins';
38d307f52SEvan Baconimport JsonFile, { JSONObject } from '@expo/json-file';
48d307f52SEvan Baconimport path from 'path';
58d307f52SEvan Bacon
68d307f52SEvan Baconimport * as Log from '../log';
78d307f52SEvan Baconimport { CommandError } from '../utils/errors';
88d307f52SEvan Baconimport {
98d307f52SEvan Bacon  getOrPromptForBundleIdentifier,
108d307f52SEvan Bacon  getOrPromptForPackage,
118d307f52SEvan Bacon} from '../utils/getOrPromptApplicationId';
128d307f52SEvan Bacon
138d307f52SEvan Bacon/**
148d307f52SEvan Bacon * If an Expo config file does not exist, write a new one using the in-memory config.
158d307f52SEvan Bacon *
168d307f52SEvan Bacon * @param projectRoot
178d307f52SEvan Bacon */
188d307f52SEvan Baconexport async function ensureConfigExistsAsync(projectRoot: string) {
198d307f52SEvan Bacon  try {
208d307f52SEvan Bacon    const config = getConfig(projectRoot, { skipSDKVersionRequirement: false });
218d307f52SEvan Bacon    // If no config exists in the file system then we should generate one so the process doesn't fail.
228d307f52SEvan Bacon    if (!config.dynamicConfigPath && !config.staticConfigPath) {
238d307f52SEvan Bacon      // Remove the internal object before writing.
248d307f52SEvan Bacon      delete config.exp._internal;
258d307f52SEvan Bacon
268d307f52SEvan Bacon      // Write the generated config.
278d307f52SEvan Bacon      await JsonFile.writeAsync(
288d307f52SEvan Bacon        path.join(projectRoot, 'app.json'),
298d307f52SEvan Bacon        { expo: config.exp as unknown as JSONObject },
308d307f52SEvan Bacon        { json5: false }
318d307f52SEvan Bacon      );
328d307f52SEvan Bacon    }
3329975bfdSEvan Bacon  } catch (error: any) {
348d307f52SEvan Bacon    // TODO(Bacon): Currently this is already handled in the command
358d307f52SEvan Bacon    Log.log();
368d307f52SEvan Bacon    throw new CommandError(`${error.message}\n`);
378d307f52SEvan Bacon  }
388d307f52SEvan Bacon}
398d307f52SEvan Bacon
40*4149568cSEvan Bacon/** Ensure config is written, and prompts for application identifiers. */
418d307f52SEvan Baconexport async function ensureConfigAsync(
428d307f52SEvan Bacon  projectRoot: string,
438d307f52SEvan Bacon  {
448d307f52SEvan Bacon    platforms,
458d307f52SEvan Bacon  }: {
468d307f52SEvan Bacon    platforms: ModPlatform[];
478d307f52SEvan Bacon  }
488d307f52SEvan Bacon): Promise<{ exp: ExpoConfig; pkg: PackageJSONConfig }> {
498d307f52SEvan Bacon  await ensureConfigExistsAsync(projectRoot);
508d307f52SEvan Bacon
518d307f52SEvan Bacon  // Prompt for the Android package first because it's more strict than the bundle identifier
528d307f52SEvan Bacon  // this means you'll have a better chance at matching the bundle identifier with the package name.
538d307f52SEvan Bacon  if (platforms.includes('android')) {
548d307f52SEvan Bacon    await getOrPromptForPackage(projectRoot);
558d307f52SEvan Bacon  }
568d307f52SEvan Bacon
578d307f52SEvan Bacon  if (platforms.includes('ios')) {
588d307f52SEvan Bacon    await getOrPromptForBundleIdentifier(projectRoot);
598d307f52SEvan Bacon  }
608d307f52SEvan Bacon
618d307f52SEvan Bacon  // Read config again because prompting for bundle id or package name may have mutated the results.
62f479be69SEvan Bacon  return getConfig(projectRoot);
638d307f52SEvan Bacon}
64