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