18d307f52SEvan Baconimport { ExpoConfig } from '@expo/config'; 28d307f52SEvan Baconimport { ModPlatform } from '@expo/config-plugins'; 38d307f52SEvan Bacon 48a424bebSJames Ideimport { clearNativeFolder, promptToClearMalformedNativeProjectsAsync } from './clearNativeFolder'; 58a424bebSJames Ideimport { configureProjectAsync } from './configureProjectAsync'; 68a424bebSJames Ideimport { ensureConfigAsync } from './ensureConfigAsync'; 78a424bebSJames Ideimport { assertPlatforms, ensureValidPlatforms, resolveTemplateOption } from './resolveOptions'; 88a424bebSJames Ideimport { updateFromTemplateAsync } from './updateFromTemplate'; 96caf5755SEvan Baconimport { installAsync } from '../install/installAsync'; 106caf5755SEvan Baconimport { env } from '../utils/env'; 112dd43328SEvan Baconimport { setNodeEnv } from '../utils/nodeEnv'; 126caf5755SEvan Baconimport { clearNodeModulesAsync } from '../utils/nodeModules'; 138d307f52SEvan Baconimport { logNewSection } from '../utils/ora'; 148d307f52SEvan Baconimport { profile } from '../utils/profile'; 158d307f52SEvan Bacon 16474a7a4bSEvan Baconconst debug = require('debug')('expo:prebuild') as typeof console.log; 17474a7a4bSEvan Bacon 188d307f52SEvan Baconexport type PrebuildResults = { 198d307f52SEvan Bacon /** Expo config. */ 208d307f52SEvan Bacon exp: ExpoConfig; 218d307f52SEvan Bacon /** Indicates if the process created new files. */ 228d307f52SEvan Bacon hasNewProjectFiles: boolean; 238d307f52SEvan Bacon /** The platforms that were prebuilt. */ 248d307f52SEvan Bacon platforms: ModPlatform[]; 258d307f52SEvan Bacon /** Indicates if pod install was run. */ 268d307f52SEvan Bacon podInstall: boolean; 278d307f52SEvan Bacon /** Indicates if node modules were installed. */ 288d307f52SEvan Bacon nodeInstall: boolean; 298d307f52SEvan Bacon}; 308d307f52SEvan Bacon 318d307f52SEvan Bacon/** 328d307f52SEvan Bacon * Entry point into the prebuild process, delegates to other helpers to perform various steps. 338d307f52SEvan Bacon * 348d307f52SEvan Bacon * 0. Attempt to clean the project folders. 358d307f52SEvan Bacon * 1. Create native projects (ios, android). 368d307f52SEvan Bacon * 2. Install node modules. 378d307f52SEvan Bacon * 3. Apply config to native projects. 388d307f52SEvan Bacon * 4. Install CocoaPods. 398d307f52SEvan Bacon */ 408d307f52SEvan Baconexport async function prebuildAsync( 418d307f52SEvan Bacon projectRoot: string, 428d307f52SEvan Bacon options: { 438d307f52SEvan Bacon /** Should install node modules and cocoapods. */ 443d6e487dSEvan Bacon install?: boolean; 458d307f52SEvan Bacon /** List of platforms to prebuild. */ 468d307f52SEvan Bacon platforms: ModPlatform[]; 478d307f52SEvan Bacon /** Should delete the native folders before attempting to prebuild. */ 488d307f52SEvan Bacon clean?: boolean; 498d307f52SEvan Bacon /** URL or file path to the prebuild template. */ 508d307f52SEvan Bacon template?: string; 518d307f52SEvan Bacon /** Name of the node package manager to install with. */ 526caf5755SEvan Bacon packageManager?: { 536caf5755SEvan Bacon npm?: boolean; 546caf5755SEvan Bacon yarn?: boolean; 556caf5755SEvan Bacon pnpm?: boolean; 569b1b5ec6SEvan Bacon bun?: boolean; 576caf5755SEvan Bacon }; 588d307f52SEvan Bacon /** List of node modules to skip updating. */ 598d307f52SEvan Bacon skipDependencyUpdate?: string[]; 608d307f52SEvan Bacon } 6129975bfdSEvan Bacon): Promise<PrebuildResults | null> { 622dd43328SEvan Bacon setNodeEnv('development'); 636a750d06SEvan Bacon require('@expo/env').load(projectRoot); 642dd43328SEvan Bacon 658d307f52SEvan Bacon if (options.clean) { 66*1a3a1db5SEvan Bacon const { maybeBailOnGitStatusAsync } = await import('../utils/git.js'); 678d307f52SEvan Bacon // Clean the project folders... 688d307f52SEvan Bacon if (await maybeBailOnGitStatusAsync()) { 6929975bfdSEvan Bacon return null; 708d307f52SEvan Bacon } 718d307f52SEvan Bacon // Clear the native folders before syncing 728d307f52SEvan Bacon await clearNativeFolder(projectRoot, options.platforms); 738d307f52SEvan Bacon } else { 748d307f52SEvan Bacon // Check if the existing project folders are malformed. 758d307f52SEvan Bacon await promptToClearMalformedNativeProjectsAsync(projectRoot, options.platforms); 768d307f52SEvan Bacon } 778d307f52SEvan Bacon 788d307f52SEvan Bacon // Warn if the project is attempting to prebuild an unsupported platform (iOS on Windows). 798d307f52SEvan Bacon options.platforms = ensureValidPlatforms(options.platforms); 808d307f52SEvan Bacon // Assert if no platforms are left over after filtering. 818d307f52SEvan Bacon assertPlatforms(options.platforms); 828d307f52SEvan Bacon 838d307f52SEvan Bacon // Get the Expo config, create it if missing. 848d307f52SEvan Bacon const { exp, pkg } = await ensureConfigAsync(projectRoot, { platforms: options.platforms }); 858d307f52SEvan Bacon 868d307f52SEvan Bacon // Create native projects from template. 878d307f52SEvan Bacon const { hasNewProjectFiles, needsPodInstall, hasNewDependencies } = await updateFromTemplateAsync( 888d307f52SEvan Bacon projectRoot, 898d307f52SEvan Bacon { 908d307f52SEvan Bacon exp, 918d307f52SEvan Bacon pkg, 928d307f52SEvan Bacon template: options.template != null ? resolveTemplateOption(options.template) : undefined, 938d307f52SEvan Bacon platforms: options.platforms, 948d307f52SEvan Bacon skipDependencyUpdate: options.skipDependencyUpdate, 958d307f52SEvan Bacon } 968d307f52SEvan Bacon ); 978d307f52SEvan Bacon 988d307f52SEvan Bacon // Install node modules 998d307f52SEvan Bacon if (options.install) { 1006caf5755SEvan Bacon if (hasNewDependencies && options.packageManager?.npm) { 1016caf5755SEvan Bacon await clearNodeModulesAsync(projectRoot); 1026caf5755SEvan Bacon } 1036caf5755SEvan Bacon 1046caf5755SEvan Bacon await installAsync([], { 105f479be69SEvan Bacon npm: !!options.packageManager?.npm, 106f479be69SEvan Bacon yarn: !!options.packageManager?.yarn, 107f479be69SEvan Bacon pnpm: !!options.packageManager?.pnpm, 1089b1b5ec6SEvan Bacon bun: !!options.packageManager?.bun, 1095417038cSCedric van Putten silent: !(env.EXPO_DEBUG || env.CI), 1108d307f52SEvan Bacon }); 1118d307f52SEvan Bacon } 1128d307f52SEvan Bacon 1138d307f52SEvan Bacon // Apply Expo config to native projects 1148d307f52SEvan Bacon const configSyncingStep = logNewSection('Config syncing'); 1158d307f52SEvan Bacon try { 1168d307f52SEvan Bacon await profile(configureProjectAsync)(projectRoot, { 1178d307f52SEvan Bacon platforms: options.platforms, 1188d307f52SEvan Bacon }); 1198d307f52SEvan Bacon configSyncingStep.succeed('Config synced'); 1208d307f52SEvan Bacon } catch (error) { 1218d307f52SEvan Bacon configSyncingStep.fail('Config sync failed'); 1228d307f52SEvan Bacon throw error; 1238d307f52SEvan Bacon } 1248d307f52SEvan Bacon 1258d307f52SEvan Bacon // Install CocoaPods 1268d307f52SEvan Bacon let podsInstalled: boolean = false; 1278d307f52SEvan Bacon // err towards running pod install less because it's slow and users can easily run npx pod-install afterwards. 1288d307f52SEvan Bacon if (options.platforms.includes('ios') && options.install && needsPodInstall) { 129*1a3a1db5SEvan Bacon const { installCocoaPodsAsync } = await import('../utils/cocoapods.js'); 1308d307f52SEvan Bacon 1318d307f52SEvan Bacon podsInstalled = await installCocoaPodsAsync(projectRoot); 1328d307f52SEvan Bacon } else { 133474a7a4bSEvan Bacon debug('Skipped pod install'); 1348d307f52SEvan Bacon } 1358d307f52SEvan Bacon 1368d307f52SEvan Bacon return { 1373d6e487dSEvan Bacon nodeInstall: !!options.install, 1388d307f52SEvan Bacon podInstall: !podsInstalled, 1398d307f52SEvan Bacon platforms: options.platforms, 1408d307f52SEvan Bacon hasNewProjectFiles, 1418d307f52SEvan Bacon exp, 1428d307f52SEvan Bacon }; 1438d307f52SEvan Bacon} 144