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 npm: !!options.packageManager?.npm, 104 yarn: !!options.packageManager?.yarn, 105 pnpm: !!options.packageManager?.pnpm, 106 silent: !env.EXPO_DEBUG, 107 }); 108 } 109 110 // Apply Expo config to native projects 111 const configSyncingStep = logNewSection('Config syncing'); 112 try { 113 await profile(configureProjectAsync)(projectRoot, { 114 platforms: options.platforms, 115 }); 116 configSyncingStep.succeed('Config synced'); 117 } catch (error) { 118 configSyncingStep.fail('Config sync failed'); 119 throw error; 120 } 121 122 // Install CocoaPods 123 let podsInstalled: boolean = false; 124 // err towards running pod install less because it's slow and users can easily run npx pod-install afterwards. 125 if (options.platforms.includes('ios') && options.install && needsPodInstall) { 126 const { installCocoaPodsAsync } = await import('../utils/cocoapods'); 127 128 podsInstalled = await installCocoaPodsAsync(projectRoot); 129 } else { 130 debug('Skipped pod install'); 131 } 132 133 return { 134 nodeInstall: !!options.install, 135 podInstall: !podsInstalled, 136 platforms: options.platforms, 137 hasNewProjectFiles, 138 exp, 139 }; 140} 141