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 await AndroidVersioning.addVersionAsync(sdkVersion); 84 await AndroidVersioning.versionVendoredModulesAsync(sdkNumber, null); 85 return; 86 default: 87 throw new Error(`Platform '${options.platform}' is not supported.`); 88 } 89} 90 91export default (program: Command) => { 92 program 93 .command('add-sdk-version') 94 .alias('add-sdk') 95 .description('Versions code for the new SDK version.') 96 .usage( 97 ` 98 99To version code for the new SDK on iOS, run: 100${chalk.gray('>')} ${chalk.italic.cyan('et add-sdk-version --platform ios')} 101 102To backport changes made in unversioned code into already versioned SDK, run: 103${chalk.gray('>')} ${chalk.italic.cyan( 104 'et add-sdk-version --platform ios --sdkVersion XX.0.0 --filenames */some/glob/expression/**' 105 )}` 106 ) 107 .option( 108 '-p, --platform <string>', 109 `Specifies a platform for which the SDK code should be generated. Supported platforms: ${chalk.cyan( 110 'ios' 111 )}.` 112 ) 113 .option( 114 '-s, --sdkVersion [string]', 115 'SDK version to add. Can be a full version name, major number or `next` tag. Defaults to `next` on the CI.' 116 ) 117 .option( 118 '-f, --filenames [string]', 119 '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.' 120 ) 121 .option( 122 '-v, --vendored <string>', 123 'Name of the vendored module to (re)version. iOS only.', 124 (value, previous) => (previous ?? []).concat(value), 125 [] 126 ) 127 .option( 128 '-r, --reinstall', 129 'Whether to force reinstalling pods after generating a new version. iOS only.' 130 ) 131 .option( 132 '--prevent-reinstall', 133 'Whether to force not reinstalling pods after generating a new version. iOS only.' 134 ) 135 .option('-x, --packages <string...>', 'Name of the expo package to version.') 136 .asyncAction(action); 137}; 138