1import { ExpoConfig } from '@expo/config'; 2import { ModPlatform } from '@expo/config-plugins'; 3 4import { installAsync } from '../install/installAsync'; 5import * as Log from '../log'; 6import { env } from '../utils/env'; 7import { clearNodeModulesAsync } from '../utils/nodeModules'; 8import { logNewSection } from '../utils/ora'; 9import { profile } from '../utils/profile'; 10import { clearNativeFolder, promptToClearMalformedNativeProjectsAsync } from './clearNativeFolder'; 11import { configureProjectAsync } from './configureProjectAsync'; 12import { ensureConfigAsync } from './ensureConfigAsync'; 13import { assertPlatforms, ensureValidPlatforms, resolveTemplateOption } from './resolveOptions'; 14import { updateFromTemplateAsync } from './updateFromTemplate'; 15 16export type PrebuildResults = { 17 /** Expo config. */ 18 exp: ExpoConfig; 19 /** Indicates if the process created new files. */ 20 hasNewProjectFiles: boolean; 21 /** The platforms that were prebuilt. */ 22 platforms: ModPlatform[]; 23 /** Indicates if pod install was run. */ 24 podInstall: boolean; 25 /** Indicates if node modules were installed. */ 26 nodeInstall: boolean; 27}; 28 29/** 30 * Entry point into the prebuild process, delegates to other helpers to perform various steps. 31 * 32 * 0. Attempt to clean the project folders. 33 * 1. Create native projects (ios, android). 34 * 2. Install node modules. 35 * 3. Apply config to native projects. 36 * 4. Install CocoaPods. 37 */ 38export async function prebuildAsync( 39 projectRoot: string, 40 options: { 41 /** Should install node modules and cocoapods. */ 42 install?: boolean; 43 /** List of platforms to prebuild. */ 44 platforms: ModPlatform[]; 45 /** Should delete the native folders before attempting to prebuild. */ 46 clean?: boolean; 47 /** URL or file path to the prebuild template. */ 48 template?: string; 49 /** Name of the node package manager to install with. */ 50 packageManager?: { 51 npm?: boolean; 52 yarn?: boolean; 53 pnpm?: boolean; 54 }; 55 /** List of node modules to skip updating. */ 56 skipDependencyUpdate?: string[]; 57 } 58): Promise<PrebuildResults | null> { 59 if (options.clean) { 60 const { maybeBailOnGitStatusAsync } = await import('../utils/git'); 61 // Clean the project folders... 62 if (await maybeBailOnGitStatusAsync()) { 63 return null; 64 } 65 // Clear the native folders before syncing 66 await clearNativeFolder(projectRoot, options.platforms); 67 } else { 68 // Check if the existing project folders are malformed. 69 await promptToClearMalformedNativeProjectsAsync(projectRoot, options.platforms); 70 } 71 72 // Warn if the project is attempting to prebuild an unsupported platform (iOS on Windows). 73 options.platforms = ensureValidPlatforms(options.platforms); 74 // Assert if no platforms are left over after filtering. 75 assertPlatforms(options.platforms); 76 77 // Get the Expo config, create it if missing. 78 const { exp, pkg } = await ensureConfigAsync(projectRoot, { platforms: options.platforms }); 79 80 // Create native projects from template. 81 const { hasNewProjectFiles, needsPodInstall, hasNewDependencies } = await updateFromTemplateAsync( 82 projectRoot, 83 { 84 exp, 85 pkg, 86 template: options.template != null ? resolveTemplateOption(options.template) : undefined, 87 platforms: options.platforms, 88 skipDependencyUpdate: options.skipDependencyUpdate, 89 } 90 ); 91 92 // Install node modules 93 if (options.install) { 94 if (hasNewDependencies && options.packageManager?.npm) { 95 await clearNodeModulesAsync(projectRoot); 96 } 97 98 await installAsync([], { 99 ...options.packageManager, 100 silent: !env.EXPO_DEBUG, 101 }); 102 } 103 104 // Apply Expo config to native projects 105 const configSyncingStep = logNewSection('Config syncing'); 106 try { 107 await profile(configureProjectAsync)(projectRoot, { 108 platforms: options.platforms, 109 }); 110 configSyncingStep.succeed('Config synced'); 111 } catch (error) { 112 configSyncingStep.fail('Config sync failed'); 113 throw error; 114 } 115 116 // Install CocoaPods 117 let podsInstalled: boolean = false; 118 // err towards running pod install less because it's slow and users can easily run npx pod-install afterwards. 119 if (options.platforms.includes('ios') && options.install && needsPodInstall) { 120 const { installCocoaPodsAsync } = await import('../utils/cocoapods'); 121 122 podsInstalled = await installCocoaPodsAsync(projectRoot); 123 } else { 124 Log.debug('Skipped pod install'); 125 } 126 127 return { 128 nodeInstall: !!options.install, 129 podInstall: !podsInstalled, 130 platforms: options.platforms, 131 hasNewProjectFiles, 132 exp, 133 }; 134} 135