1import chalk from 'chalk'; 2import { PromisyClass, TaskQueue } from 'cwait'; 3import fs from 'fs-extra'; 4import os from 'os'; 5import path from 'path'; 6 7import logger from '../../Logger'; 8import { Package } from '../../Packages'; 9import { copyFileWithTransformsAsync } from '../../Transforms'; 10import { searchFilesAsync } from '../../Utils'; 11import { expoModulesTransforms } from './transforms/expoModulesTransforms'; 12import { getVersionPrefix, getVersionedDirectory } from './utils'; 13 14// Label of the console's timer used during versioning 15const TIMER_LABEL = 'Versioning expo modules finished in'; 16 17// List of pod names to exclude from versioning 18const EXCLUDED_POD_NAMES = ['EXPaymentsStripe']; 19 20// The pattern that matches the dependency pods that need to be renamed in `*.podspec.json`. 21const PODSPEC_DEPS_TO_RENAME_PATTERN = /^(Expo|EX(?!GL_CPP)|UM|React|RCT|Yoga)/; 22 23/** 24 * Function that versions expo modules. 25 */ 26export async function versionExpoModulesAsync( 27 sdkNumber: number, 28 packages: Package[] 29): Promise<void> { 30 const prefix = getVersionPrefix(sdkNumber); 31 const transforms = expoModulesTransforms(prefix); 32 const versionedDirectory = getVersionedDirectory(sdkNumber); 33 const taskQueue = new TaskQueue(Promise as PromisyClass, os.cpus().length); 34 35 // Prepare versioning task (for single package). 36 const versionPackageTask = taskQueue.wrap(async (pkg) => { 37 if (EXCLUDED_POD_NAMES.includes(pkg.podspecName)) { 38 return; 39 } 40 41 logger.log(`- ${chalk.green(pkg.podspecName!)}`); 42 const sourceDirectory = path.join(pkg.path, pkg.iosSubdirectory); 43 const targetDirectory = path.join(versionedDirectory, pkg.podspecName!); 44 45 // Find all iOS files within the package, except the podspec. 46 // Podspecs depend on the corresponding `package.json`, 47 // that we don't want to copy (no need to version JS files, workspace project names duplication). 48 // Instead, we generate the static podspec in JSON format (see `generateVersionedPodspecAsync`). 49 const files = await searchFilesAsync(sourceDirectory, '**', { 50 ignore: [`${pkg.podspecName}.podspec`], 51 }); 52 53 // Ensure the target directory is empty 54 if (await fs.pathExists(targetDirectory)) { 55 await fs.remove(targetDirectory); 56 } 57 58 // Copy files to the new directory with applied transforms 59 for (const sourceFile of files) { 60 await copyFileWithTransformsAsync({ 61 sourceFile, 62 targetDirectory, 63 sourceDirectory, 64 transforms, 65 }); 66 } 67 68 // Create a podspec in JSON format so we don't have to keep `package.json`s 69 await generateVersionedPodspecAsync(pkg, prefix, targetDirectory); 70 }); 71 72 logger.info(' Versioning expo modules'); 73 console.time(TIMER_LABEL); 74 75 // Enqueue packages to version. 76 await Promise.all(packages.map(versionPackageTask)); 77 78 console.timeEnd(TIMER_LABEL); 79} 80 81/** 82 * Generates versioned static podspec in JSON format. 83 */ 84async function generateVersionedPodspecAsync( 85 pkg: Package, 86 prefix: string, 87 targetDirectory: string 88) { 89 const podspec = await pkg.getPodspecAsync(); 90 91 if (!podspec) { 92 throw new Error(`Podspec for package ${pkg.packageName} not found`); 93 } 94 if (podspec.name) { 95 podspec.name = `${prefix}${podspec.name}`; 96 } 97 if (podspec.header_dir) { 98 podspec.header_dir = `${prefix}${podspec.header_dir}`; 99 } 100 if (podspec.dependencies) { 101 Object.keys(podspec.dependencies) 102 .filter( 103 (key) => !EXCLUDED_POD_NAMES.includes(key) && PODSPEC_DEPS_TO_RENAME_PATTERN.test(key) 104 ) 105 .forEach((key) => { 106 const newKey = `${prefix}${key}`; 107 podspec.dependencies[newKey] = podspec.dependencies[key]; 108 delete podspec.dependencies[key]; 109 }); 110 } 111 112 // Remove script phases. 113 // expo-constants has one, but the `scripts` folder isn't being copied and there is no need to have it in versioned code. 114 delete podspec['script_phases']; 115 116 const targetPath = path.join(targetDirectory, `${prefix}${pkg.podspecName}.podspec.json`); 117 118 // Write a new one 119 await fs.outputJson(targetPath, podspec, { 120 spaces: 2, 121 }); 122} 123