1import { Command } from '@expo/commander'; 2import chalk from 'chalk'; 3import inquirer from 'inquirer'; 4import semver from 'semver'; 5 6import { getListOfPackagesAsync } from '../Packages'; 7import { Platform, getNextSDKVersionAsync, resolveSDKVersionAsync } from '../ProjectVersions'; 8import * as AndroidVersioning from '../versioning/android'; 9import * as IosVersioning from '../versioning/ios'; 10 11type ActionOptions = { 12 platform: Platform; 13 sdkVersion?: string; 14 filenames?: string; 15 vendored: string[]; 16 reinstall?: boolean; 17 preventReinstall?: boolean; 18 packages?: string[]; 19}; 20 21async function getNextOrAskForSDKVersionAsync(platform: Platform): Promise<string | undefined> { 22 const defaultSdkVersion = await getNextSDKVersionAsync(platform); 23 24 if (defaultSdkVersion && process.env.CI) { 25 console.log( 26 `${chalk.red('`--sdkVersion`')} not provided - defaulting to ${chalk.cyan(defaultSdkVersion)}` 27 ); 28 return defaultSdkVersion; 29 } 30 31 const { sdkVersion } = await inquirer.prompt<{ sdkVersion: string }>([ 32 { 33 type: 'input', 34 name: 'sdkVersion', 35 message: 'What is the SDK version that you want to add?', 36 default: defaultSdkVersion, 37 validate(value) { 38 if (!semver.valid(value)) { 39 return `Invalid version: ${chalk.cyan(value)}`; 40 } 41 return true; 42 }, 43 }, 44 ]); 45 return sdkVersion; 46} 47 48async function action(options: ActionOptions) { 49 if (!options.platform) { 50 throw new Error('Run with `--platform <ios | android>`.'); 51 } 52 53 const sdkVersion = 54 (options.sdkVersion && (await resolveSDKVersionAsync(options.sdkVersion, options.platform))) || 55 (await getNextOrAskForSDKVersionAsync(options.platform)); 56 57 if (!sdkVersion) { 58 throw new Error('Next SDK version not found. Try to run with `--sdkVersion <SDK version>`.'); 59 } 60 const sdkNumber = semver.major(sdkVersion); 61 const packages = (await getListOfPackagesAsync()).filter((pkg) => 62 pkg.isVersionableOnPlatform(options.platform) 63 ); 64 65 switch (options.platform) { 66 case 'ios': 67 if (options.vendored.length > 0) { 68 await IosVersioning.versionVendoredModulesAsync(sdkNumber, options.vendored); 69 } else if (options.filenames) { 70 await IosVersioning.versionReactNativeIOSFilesAsync(options.filenames, sdkVersion); 71 } else if (options.packages) { 72 await IosVersioning.versionExpoModulesAsync( 73 sdkNumber, 74 packages.filter((pkg) => options.packages?.includes(pkg.packageName)) 75 ); 76 } else { 77 await IosVersioning.versionVendoredModulesAsync(sdkNumber, null); 78 await IosVersioning.addVersionAsync(sdkVersion, packages); 79 } 80 await IosVersioning.reinstallPodsAsync(options.reinstall, options.preventReinstall); 81 return; 82 case 'android': 83 return AndroidVersioning.addVersionAsync(sdkVersion); 84 default: 85 throw new Error(`Platform '${options.platform}' is not supported.`); 86 } 87} 88 89export default (program: Command) => { 90 program 91 .command('add-sdk-version') 92 .alias('add-sdk') 93 .description('Versions code for the new SDK version.') 94 .usage( 95 ` 96 97To version code for the new SDK on iOS, run: 98${chalk.gray('>')} ${chalk.italic.cyan('et add-sdk-version --platform ios')} 99 100To backport changes made in unversioned code into already versioned SDK, run: 101${chalk.gray('>')} ${chalk.italic.cyan( 102 'et add-sdk-version --platform ios --sdkVersion XX.0.0 --filenames */some/glob/expression/**' 103 )}` 104 ) 105 .option( 106 '-p, --platform <string>', 107 `Specifies a platform for which the SDK code should be generated. Supported platforms: ${chalk.cyan( 108 'ios' 109 )}.` 110 ) 111 .option( 112 '-s, --sdkVersion [string]', 113 'SDK version to add. Can be a full version name, major number or `next` tag. Defaults to `next` on the CI.' 114 ) 115 .option( 116 '-f, --filenames [string]', 117 'Glob pattern of file paths to version. Useful when you want to backport unversioned code into already versioned SDK. Optional. When provided, option `--sdkVersion` is required.' 118 ) 119 .option( 120 '-v, --vendored <string>', 121 'Name of the vendored module to (re)version. iOS only.', 122 (value, previous) => (previous ?? []).concat(value), 123 [] 124 ) 125 .option( 126 '-r, --reinstall', 127 'Whether to force reinstalling pods after generating a new version. iOS only.' 128 ) 129 .option( 130 '--prevent-reinstall', 131 'Whether to force not reinstalling pods after generating a new version. iOS only.' 132 ) 133 .option('-x, --packages <string...>', 'Name of the expo package to version.') 134 .asyncAction(action); 135}; 136