1import path from 'path'; 2import fs from 'fs-extra'; 3import chalk from 'chalk'; 4import { Command } from '@expo/commander'; 5import spawnAsync from '@expo/spawn-async'; 6 7import { EXPO_DIR, ANDROID_DIR } from '../Constants'; 8import { getNextSDKVersionAsync } from '../ProjectVersions'; 9import { getReactNativeSubmoduleDir } from '../Directories'; 10 11type ActionOptions = { 12 checkout?: string; 13 sdkVersion?: string; 14}; 15 16const REACT_NATIVE_SUBMODULE_PATH = getReactNativeSubmoduleDir(); 17const REACT_ANDROID_PATH = path.join(ANDROID_DIR, 'ReactAndroid'); 18const REACT_COMMON_PATH = path.join(ANDROID_DIR, 'ReactCommon'); 19 20async function checkoutReactNativeSubmoduleAsync(checkoutRef: string): Promise<void> { 21 await spawnAsync('git', ['fetch'], { 22 cwd: REACT_NATIVE_SUBMODULE_PATH, 23 }); 24 await spawnAsync('git', ['checkout', checkoutRef], { 25 cwd: REACT_NATIVE_SUBMODULE_PATH, 26 }); 27} 28 29async function updateReactAndroidAsync(sdkVersion: string): Promise<void> { 30 console.log(`Cleaning ${chalk.magenta(path.relative(EXPO_DIR, REACT_ANDROID_PATH))}...`); 31 await fs.remove(REACT_ANDROID_PATH); 32 33 console.log(`Cleaning ${chalk.magenta(path.relative(EXPO_DIR, REACT_COMMON_PATH))}...`); 34 await fs.remove(REACT_COMMON_PATH); 35 36 console.log( 37 `Running ${chalk.blue('ReactAndroidCodeTransformer')} with ${chalk.yellow( 38 `./gradlew :tools:execute --args ${sdkVersion}` 39 )} command...` 40 ); 41 await spawnAsync('./gradlew', [':tools:execute', '--args', sdkVersion], { 42 cwd: ANDROID_DIR, 43 stdio: 'inherit', 44 }); 45} 46 47async function action(options: ActionOptions) { 48 if (options.checkout) { 49 console.log( 50 `Checking out ${chalk.magenta( 51 path.relative(EXPO_DIR, REACT_NATIVE_SUBMODULE_PATH) 52 )} submodule at ${chalk.blue(options.checkout)} ref...` 53 ); 54 await checkoutReactNativeSubmoduleAsync(options.checkout); 55 } 56 57 // When we're updating React Native, we mostly want it to be for the next SDK that isn't versioned yet. 58 const androidSdkVersion = options.sdkVersion || (await getNextSDKVersionAsync('android')); 59 60 if (!androidSdkVersion) { 61 throw new Error( 62 'Cannot obtain next SDK version. Try to run with --sdkVersion <sdkVersion> flag.' 63 ); 64 } 65 66 console.log( 67 `Updating ${chalk.green('ReactAndroid')} for SDK ${chalk.cyan(androidSdkVersion)} ...` 68 ); 69 await updateReactAndroidAsync(androidSdkVersion); 70} 71 72export default (program: Command) => { 73 program 74 .command('update-react-native') 75 .alias('update-rn', 'urn') 76 .description( 77 'Updates React Native submodule and applies Expo-specific code transformations on ReactAndroid and ReactCommon folders.' 78 ) 79 .option( 80 '-c, --checkout [string]', 81 "Git's ref to the commit, tag or branch on which the React Native submodule should be checkouted." 82 ) 83 .option( 84 '-s, --sdkVersion [string]', 85 'SDK version for which the forked React Native will be used. Defaults to the newest SDK version increased by a major update.' 86 ) 87 .asyncAction(action); 88}; 89