18d307f52SEvan Baconimport { ExpoConfig, PackageJSONConfig } from '@expo/config'; 28d307f52SEvan Baconimport { ModPlatform } from '@expo/config-plugins'; 38d307f52SEvan Baconimport chalk from 'chalk'; 48d307f52SEvan Bacon 58d307f52SEvan Baconimport { copyTemplateFilesAsync, createCopyFilesSuccessMessage } from './copyTemplateFiles'; 68d307f52SEvan Baconimport { cloneTemplateAsync } from './resolveTemplate'; 78d307f52SEvan Baconimport { DependenciesModificationResults, updatePackageJSONAsync } from './updatePackageJson'; 85492d26aSCedric van Puttenimport { validateTemplatePlatforms } from './validateTemplatePlatforms'; 98a424bebSJames Ideimport * as Log from '../log'; 108a424bebSJames Ideimport { AbortCommandError, SilentError } from '../utils/errors'; 118a424bebSJames Ideimport { logNewSection } from '../utils/ora'; 128a424bebSJames Ideimport { profile } from '../utils/profile'; 138d307f52SEvan Bacon 148d307f52SEvan Bacon/** 158d307f52SEvan Bacon * Creates local native files from an input template file path. 168d307f52SEvan Bacon * 178d307f52SEvan Bacon * @return `true` if the project is ejecting, and `false` if it's syncing. 188d307f52SEvan Bacon */ 198d307f52SEvan Baconexport async function updateFromTemplateAsync( 208d307f52SEvan Bacon projectRoot: string, 218d307f52SEvan Bacon { 228d307f52SEvan Bacon exp, 238d307f52SEvan Bacon pkg, 248d307f52SEvan Bacon template, 258d307f52SEvan Bacon templateDirectory, 268d307f52SEvan Bacon platforms, 278d307f52SEvan Bacon skipDependencyUpdate, 288d307f52SEvan Bacon }: { 298d307f52SEvan Bacon /** Expo Config */ 308d307f52SEvan Bacon exp: ExpoConfig; 318d307f52SEvan Bacon /** package.json as JSON */ 328d307f52SEvan Bacon pkg: PackageJSONConfig; 338d307f52SEvan Bacon /** Template reference ID. */ 348d307f52SEvan Bacon template?: string; 358d307f52SEvan Bacon /** Directory to write the template to before copying into the project. */ 368d307f52SEvan Bacon templateDirectory?: string; 378d307f52SEvan Bacon /** List of platforms to clone. */ 388d307f52SEvan Bacon platforms: ModPlatform[]; 398d307f52SEvan Bacon /** List of dependencies to skip updating. */ 408d307f52SEvan Bacon skipDependencyUpdate?: string[]; 418d307f52SEvan Bacon } 428d307f52SEvan Bacon): Promise< 438d307f52SEvan Bacon { 448d307f52SEvan Bacon /** Indicates if new files were created in the project. */ 458d307f52SEvan Bacon hasNewProjectFiles: boolean; 468d307f52SEvan Bacon /** Indicates that the project needs to run `pod install` */ 478d307f52SEvan Bacon needsPodInstall: boolean; 488d307f52SEvan Bacon } & DependenciesModificationResults 498d307f52SEvan Bacon> { 508d307f52SEvan Bacon if (!templateDirectory) { 518d307f52SEvan Bacon const temporary = await import('tempy'); 528d307f52SEvan Bacon templateDirectory = temporary.directory(); 538d307f52SEvan Bacon } 548d307f52SEvan Bacon 558d307f52SEvan Bacon const copiedPaths = await profile(cloneTemplateAndCopyToProjectAsync)({ 568d307f52SEvan Bacon projectRoot, 578d307f52SEvan Bacon template, 588d307f52SEvan Bacon templateDirectory, 598d307f52SEvan Bacon exp, 608d307f52SEvan Bacon platforms, 618d307f52SEvan Bacon }); 628d307f52SEvan Bacon 638d307f52SEvan Bacon const depsResults = await profile(updatePackageJSONAsync)(projectRoot, { 648d307f52SEvan Bacon templateDirectory, 658d307f52SEvan Bacon pkg, 668d307f52SEvan Bacon skipDependencyUpdate, 678d307f52SEvan Bacon }); 688d307f52SEvan Bacon 698d307f52SEvan Bacon return { 708d307f52SEvan Bacon hasNewProjectFiles: !!copiedPaths.length, 718d307f52SEvan Bacon // If the iOS folder changes or new packages are added, we should rerun pod install. 728d307f52SEvan Bacon needsPodInstall: 738d307f52SEvan Bacon copiedPaths.includes('ios') || 748d307f52SEvan Bacon depsResults.hasNewDependencies || 758d307f52SEvan Bacon depsResults.hasNewDevDependencies, 768d307f52SEvan Bacon ...depsResults, 778d307f52SEvan Bacon }; 788d307f52SEvan Bacon} 798d307f52SEvan Bacon 808d307f52SEvan Bacon/** 818d307f52SEvan Bacon * Extract the template and copy the ios and android directories over to the project directory. 828d307f52SEvan Bacon * 838d307f52SEvan Bacon * @return `true` if any project files were created. 848d307f52SEvan Bacon */ 858d307f52SEvan Baconasync function cloneTemplateAndCopyToProjectAsync({ 868d307f52SEvan Bacon projectRoot, 878d307f52SEvan Bacon templateDirectory, 888d307f52SEvan Bacon template, 898d307f52SEvan Bacon exp, 905492d26aSCedric van Putten platforms: unknownPlatforms, 918d307f52SEvan Bacon}: { 928d307f52SEvan Bacon projectRoot: string; 938d307f52SEvan Bacon templateDirectory: string; 948d307f52SEvan Bacon template?: string; 958d307f52SEvan Bacon exp: Pick<ExpoConfig, 'name' | 'sdkVersion'>; 968d307f52SEvan Bacon platforms: ModPlatform[]; 978d307f52SEvan Bacon}): Promise<string[]> { 98*9f789f00SAlan Hughes const platformDirectories = unknownPlatforms 99*9f789f00SAlan Hughes .map((platform) => `./${platform}`) 100*9f789f00SAlan Hughes .reverse() 101*9f789f00SAlan Hughes .join(' and '); 102*9f789f00SAlan Hughes 103*9f789f00SAlan Hughes const pluralized = unknownPlatforms.length > 1 ? 'directories' : 'directory'; 1048d307f52SEvan Bacon const ora = logNewSection( 105*9f789f00SAlan Hughes `Creating native project ${pluralized} (${platformDirectories}) and updating .gitignore` 1068d307f52SEvan Bacon ); 1078d307f52SEvan Bacon 1088d307f52SEvan Bacon try { 1098d307f52SEvan Bacon await cloneTemplateAsync({ templateDirectory, template, exp, ora }); 1108d307f52SEvan Bacon 1115492d26aSCedric van Putten const platforms = await validateTemplatePlatforms({ 1125492d26aSCedric van Putten templateDirectory, 1135492d26aSCedric van Putten platforms: unknownPlatforms, 1145492d26aSCedric van Putten }); 1155492d26aSCedric van Putten 1168d307f52SEvan Bacon const results = await copyTemplateFilesAsync(projectRoot, { 1178d307f52SEvan Bacon templateDirectory, 1188d307f52SEvan Bacon platforms, 1198d307f52SEvan Bacon }); 1208d307f52SEvan Bacon 1218d307f52SEvan Bacon ora.succeed(createCopyFilesSuccessMessage(platforms, results)); 1228d307f52SEvan Bacon 1238d307f52SEvan Bacon return results.copiedPaths; 1248d307f52SEvan Bacon } catch (e: any) { 1258d307f52SEvan Bacon if (!(e instanceof AbortCommandError)) { 1268d307f52SEvan Bacon Log.error(e.message); 1278d307f52SEvan Bacon } 1288d307f52SEvan Bacon ora.fail('Failed to create the native project.'); 1298d307f52SEvan Bacon Log.log( 1308d307f52SEvan Bacon chalk.yellow( 1318d307f52SEvan Bacon 'You may want to delete the `./ios` and/or `./android` directories before trying again.' 1328d307f52SEvan Bacon ) 1338d307f52SEvan Bacon ); 1348d307f52SEvan Bacon throw new SilentError(e); 1358d307f52SEvan Bacon } 1368d307f52SEvan Bacon} 137