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