From 37ecabfcca784466cc2f2501dc815c265930fec1 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Tue, 19 May 2026 10:33:35 +0100 Subject: [PATCH 1/2] Add resolution check to `isExpoCommand` shared with `spawnExpoCommand` --- packages/eas-cli/src/project/projectUtils.ts | 19 +++++++++++++++++-- packages/eas-cli/src/utils/expoCli.ts | 9 +++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/eas-cli/src/project/projectUtils.ts b/packages/eas-cli/src/project/projectUtils.ts index 355b2aa1ec..4485be2e2c 100644 --- a/packages/eas-cli/src/project/projectUtils.ts +++ b/packages/eas-cli/src/project/projectUtils.ts @@ -10,7 +10,7 @@ import { ExpoGraphqlClient } from '../commandUtils/context/contextUtils/createGr import { AccountFragment } from '../graphql/generated'; import { AppQuery } from '../graphql/queries/AppQuery'; import Log, { learnMore } from '../log'; -import { expoCommandAsync } from '../utils/expoCli'; +import { expoCommandAsync, resolveExpoCli } from '../utils/expoCli'; /** * Return a useful name describing the project config. @@ -45,7 +45,22 @@ export function isExpoNotificationsInstalled(projectDir: string): boolean { export function isExpoInstalled(projectDir: string): boolean { const packageJson = getPackageJson(projectDir); - return !!(packageJson.dependencies && 'expo' in packageJson.dependencies); + if (!!(packageJson.dependencies && 'expo' in packageJson.dependencies)) { + // NOTE(@kitten): We usually don't apply strict checks for installed packages, but + // `isExpoInstalled` is often used to check if we should call the Expo CLI, so we're + // also checking if we can resolve `expo` here + try { + return !!resolveExpoCli(projectDir); + } catch (e: any) { + if (e.code === 'MODULE_NOT_FOUND') { + return false; + } else { + throw e; + } + } + } else { + return false; + } } export function isExpoUpdatesInstalledAsDevDependency(projectDir: string): boolean { diff --git a/packages/eas-cli/src/utils/expoCli.ts b/packages/eas-cli/src/utils/expoCli.ts index 9d006dcb22..7507455235 100644 --- a/packages/eas-cli/src/utils/expoCli.ts +++ b/packages/eas-cli/src/utils/expoCli.ts @@ -78,6 +78,12 @@ export const shouldUseVersionedExpoCLIWithExplicitPlatforms = memoize( shouldUseVersionedExpoCLIWithExplicitPlatformsExpensive ); +export function resolveExpoCli(projectDir: string): string { + return ( + silentResolveFrom(projectDir, 'expo/bin/cli') ?? resolveFrom(projectDir, 'expo/bin/cli.js') + ); +} + export function spawnExpoCommand( projectDir: string, args: string[], @@ -85,8 +91,7 @@ export function spawnExpoCommand( ): spawnAsync.SpawnPromise { let expoCliPath; try { - expoCliPath = - silentResolveFrom(projectDir, 'expo/bin/cli') ?? resolveFrom(projectDir, 'expo/bin/cli.js'); + expoCliPath = resolveExpoCli(projectDir); } catch (e: any) { if (e.code === 'MODULE_NOT_FOUND') { throw new Error( From dbaa3f8b408c71331e9d62672b67b4e917325c78 Mon Sep 17 00:00:00 2001 From: Phil Pluckthun Date: Tue, 19 May 2026 10:34:28 +0100 Subject: [PATCH 2/2] Add CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bed610b90..2ebdd7767b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This is the log of notable changes to EAS CLI and related packages. ### ๐Ÿ› Bug fixes +- [eas-cli] Check that `expo` is resolvable before assuming it's installed ([#3748](https://github.com/expo/eas-cli/pull/3748) by [@kitten](https://github.com/kitten)) + ### ๐Ÿงน Chores ## [18.13.1](https://github.com/expo/eas-cli/releases/tag/v18.13.1) - 2026-05-18