1import JsonFile from '@expo/json-file'; 2import fs from 'fs-extra'; 3import path from 'path'; 4import plist from 'plist'; 5import semver from 'semver'; 6 7import { EXPO_DIR, ANDROID_DIR, PACKAGES_DIR } from './Constants'; 8 9export type Platform = 'ios' | 'android'; 10 11export type SDKVersionsObject = { 12 sdkVersions: string[]; 13}; 14 15const BUNDLED_NATIVE_MODULES_PATH = path.join(PACKAGES_DIR, 'expo', 'bundledNativeModules.json'); 16 17export async function sdkVersionAsync(): Promise<string> { 18 const packageJson = await JsonFile.readAsync(path.join(EXPO_DIR, 'packages/expo/package.json')); 19 return packageJson.version as string; 20} 21 22export async function iosAppVersionAsync(): Promise<string> { 23 const infoPlistPath = path.join(EXPO_DIR, 'ios', 'Exponent', 'Supporting', 'Info.plist'); 24 const infoPlist = plist.parse(fs.readFileSync(infoPlistPath, 'utf8')); 25 const bundleVersion = infoPlist.CFBundleShortVersionString; 26 27 if (!bundleVersion) { 28 throw new Error(`"CFBundleShortVersionString" not found in plist: ${infoPlistPath}`); 29 } 30 return bundleVersion; 31} 32 33export async function androidAppVersionAsync(): Promise<string> { 34 const buildGradlePath = path.join(ANDROID_DIR, 'app', 'build.gradle'); 35 const buildGradleContent = await fs.readFile(buildGradlePath, 'utf8'); 36 const match = buildGradleContent.match(/versionName ['"]([^'"]+?)['"]/); 37 38 if (!match) { 39 throw new Error("Can't obtain `versionName` from app's build.gradle"); 40 } 41 return match[1]; 42} 43 44export async function getHomeSDKVersionAsync(): Promise<string> { 45 const homeAppJsonPath = path.join(EXPO_DIR, 'home', 'app.json'); 46 const appJson = (await JsonFile.readAsync(homeAppJsonPath, { json5: true })) as any; 47 48 if (appJson?.expo?.sdkVersion) { 49 return appJson.expo.sdkVersion as string; 50 } 51 throw new Error(`Home's SDK version not found!`); 52} 53 54export async function getSDKVersionsAsync(platform: Platform): Promise<string[]> { 55 const sdkVersionsPath = path.join( 56 EXPO_DIR, 57 platform === 'ios' ? 'ios/Exponent/Supporting' : 'android', 58 'sdkVersions.json' 59 ); 60 61 if (!(await fs.pathExists(sdkVersionsPath))) { 62 throw new Error(`File at path "${sdkVersionsPath}" not found.`); 63 } 64 const { sdkVersions } = (await JsonFile.readAsync(sdkVersionsPath)) as SDKVersionsObject; 65 return sdkVersions; 66} 67 68export async function getOldestSDKVersionAsync(platform: Platform): Promise<string | undefined> { 69 const sdkVersions = await getSDKVersionsAsync(platform); 70 return sdkVersions.sort(semver.compare)[0]; 71} 72 73export async function getNewestSDKVersionAsync(platform: Platform): Promise<string | undefined> { 74 const sdkVersions = await getSDKVersionsAsync(platform); 75 return sdkVersions.sort(semver.rcompare)[0]; 76} 77 78export async function getNextSDKVersionAsync(platform: Platform): Promise<string | undefined> { 79 const newestVersion = await getNewestSDKVersionAsync(platform); 80 81 if (!newestVersion) { 82 return; 83 } 84 return `${semver.major(semver.inc(newestVersion, 'major')!)}.0.0`; 85} 86 87/** 88 * Resolves given SDK number or tag to appropriate version. 89 */ 90export async function resolveSDKVersionAsync( 91 sdkVersion: string, 92 platform: Platform 93): Promise<string | undefined> { 94 if (sdkVersion === 'latest') { 95 return await getNewestSDKVersionAsync(platform); 96 } 97 if (sdkVersion === 'oldest') { 98 return await getOldestSDKVersionAsync(platform); 99 } 100 if (sdkVersion === 'next') { 101 return await getNextSDKVersionAsync(platform); 102 } 103 if (/^\d+$/.test(sdkVersion)) { 104 return `${sdkVersion}.0.0`; 105 } 106 return sdkVersion; 107} 108 109/** 110 * Returns an object with versions of bundled native modules. 111 */ 112export async function getBundledVersionsAsync(): Promise<Record<string, string>> { 113 return require(BUNDLED_NATIVE_MODULES_PATH) as Record<string, string>; 114} 115 116/** 117 * Updates bundled native modules versions. 118 */ 119export async function updateBundledVersionsAsync(patch: Record<string, string>): Promise<void> { 120 await JsonFile.mergeAsync(BUNDLED_NATIVE_MODULES_PATH, patch); 121} 122