1import { getConfig } from '@expo/config'; 2import * as PackageManager from '@expo/package-manager'; 3import chalk from 'chalk'; 4 5import * as Log from '../log'; 6import { 7 getVersionedDependenciesAsync, 8 logIncorrectDependencies, 9} from '../start/doctor/dependencies/validateDependenciesVersions'; 10import { isInteractive } from '../utils/interactive'; 11import { confirmAsync } from '../utils/prompts'; 12import { installPackagesAsync } from './installAsync'; 13import { Options } from './resolveOptions'; 14 15const debug = require('debug')('expo:install:check') as typeof console.log; 16 17// Exposed for testing. 18export async function checkPackagesAsync( 19 projectRoot: string, 20 { 21 packages, 22 packageManager, 23 options: { fix }, 24 packageManagerArguments, 25 }: { 26 /** 27 * List of packages to version 28 * @example ['uuid', 'react-native-reanimated@latest'] 29 */ 30 packages: string[]; 31 /** Package manager to use when installing the versioned packages. */ 32 packageManager: 33 | PackageManager.NpmPackageManager 34 | PackageManager.YarnPackageManager 35 | PackageManager.PnpmPackageManager; 36 37 /** How the check should resolve */ 38 options: Pick<Options, 'fix'>; 39 /** 40 * Extra parameters to pass to the `packageManager` when installing versioned packages. 41 * @example ['--no-save'] 42 */ 43 packageManagerArguments: string[]; 44 } 45) { 46 // Read the project Expo config without plugins. 47 const { exp, pkg } = getConfig(projectRoot, { 48 // Sometimes users will add a plugin to the config before installing the library, 49 // this wouldn't work unless we dangerously disable plugin serialization. 50 skipPlugins: true, 51 }); 52 53 const dependencies = await getVersionedDependenciesAsync(projectRoot, exp, pkg, packages); 54 55 if (!dependencies.length) { 56 Log.exit(chalk.greenBright('Dependencies are up to date'), 0); 57 } 58 59 logIncorrectDependencies(dependencies); 60 61 const value = 62 // If `--fix` then always fix. 63 fix || 64 // Otherwise prompt to fix when not running in CI. 65 (isInteractive() && (await confirmAsync({ message: 'Fix dependencies?' }).catch(() => false))); 66 67 if (value) { 68 // Just pass in the names, the install function will resolve the versions again. 69 const fixedDependencies = dependencies.map((dependency) => dependency.packageName); 70 debug('Installing fixed dependencies:', fixedDependencies); 71 // Install the corrected dependencies. 72 return installPackagesAsync(projectRoot, { 73 packageManager, 74 packages: fixedDependencies, 75 packageManagerArguments, 76 sdkVersion: exp.sdkVersion!, 77 }); 78 } 79 // Exit with non-zero exit code if any of the dependencies are out of date. 80 Log.exit(chalk.red('Found outdated dependencies'), 1); 81} 82