Skip to content

Commit 490bfd6

Browse files
jithinv-devclaude
andcommitted
fix(expo): resolve Expo CLI via expo/bin/cli for SDK 55+ executors
Expo SDK 55+ publishes @expo/cli with an exports map ("./*": "./*.js"), so require.resolve('@expo/cli/build/bin/cli') now fails with MODULE_NOT_FOUND because the bin file 'build/bin/cli' has no .js extension. This broke the start, run, export, serve and prebuild executors (e.g. 'nx prebuild' → Cannot find module '.../@expo/cli/build/bin/cli.js'). Resolve the CLI via the stable 'expo/bin/cli' entry point (the same one 'npx expo' uses), falling back to the legacy path for older SDKs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 358f780 commit 490bfd6

6 files changed

Lines changed: 27 additions & 5 deletions

File tree

packages/expo/src/executors/export/export.impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { resolve as pathResolve } from 'path';
1010

1111
import { ExportExecutorSchema } from './schema';
1212
import { warnExpoExecutorDeprecation } from '../../utils/deprecation';
13+
import { resolveExpoCliPath } from '../../utils/resolve-expo-cli';
1314

1415
export interface ExpoExportOutput {
1516
success: boolean;
@@ -45,7 +46,7 @@ function exportAsync(
4546
): Promise<number> {
4647
return new Promise((resolve, reject) => {
4748
childProcess = fork(
48-
require.resolve('@expo/cli/build/bin/cli'),
49+
resolveExpoCliPath(),
4950
[`export`, ...createExportOptions(options, projectRoot)],
5051
{
5152
cwd: pathResolve(workspaceRoot, projectRoot),

packages/expo/src/executors/prebuild/prebuild.impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ExecutorContext, names, workspaceRoot } from '@nx/devkit';
22
import { signalToCode } from '@nx/devkit/internal';
33
import { ChildProcess, fork } from 'child_process';
4+
import { resolveExpoCliPath } from '../../utils/resolve-expo-cli';
45
import { join } from 'path';
56

67
import { podInstall } from '../../utils/pod-install-task';
@@ -52,7 +53,7 @@ export function prebuildAsync(
5253
): Promise<number> {
5354
return new Promise((resolve, reject) => {
5455
childProcess = fork(
55-
require.resolve('@expo/cli/build/bin/cli'),
56+
resolveExpoCliPath(),
5657
['prebuild', ...createPrebuildOptions(options), '--no-install'],
5758
{
5859
cwd: join(workspaceRoot, projectRoot),

packages/expo/src/executors/run/run.impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ExecutorContext, names } from '@nx/devkit';
22
import { signalToCode } from '@nx/devkit/internal';
33
import { ChildProcess, fork } from 'child_process';
4+
import { resolveExpoCliPath } from '../../utils/resolve-expo-cli';
45
import { existsSync } from 'node:fs';
56
import { platform } from 'os';
67
import { join, resolve as pathResolve } from 'path';
@@ -64,7 +65,7 @@ function runCliRun(
6465
) {
6566
return new Promise((resolve, reject) => {
6667
childProcess = fork(
67-
require.resolve('@expo/cli/build/bin/cli'),
68+
resolveExpoCliPath(),
6869
['run:' + options.platform, ...createRunOptions(options), '--no-install'], // pass in no-install to prevent node_modules install
6970
{
7071
cwd: pathResolve(workspaceRoot, projectRoot),

packages/expo/src/executors/serve/serve.impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ExecutorContext, logger, names } from '@nx/devkit';
22
import { signalToCode } from '@nx/devkit/internal';
33
import { ChildProcess, fork } from 'child_process';
4+
import { resolveExpoCliPath } from '../../utils/resolve-expo-cli';
45
import { resolve as pathResolve } from 'path';
56
import { isPackagerRunning } from './lib/is-packager-running';
67
import { ExpoServeExecutorSchema } from './schema';
@@ -77,7 +78,7 @@ function serveAsync(
7778
): Promise<ChildProcess> {
7879
return new Promise<ChildProcess>((resolve, reject) => {
7980
const childProcess = fork(
80-
require.resolve('@expo/cli/build/bin/cli'),
81+
resolveExpoCliPath(),
8182
['start', '--web', ...createServeOptions(options)],
8283
{
8384
cwd: pathResolve(workspaceRoot, projectRoot),

packages/expo/src/executors/start/start.impl.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as pc from 'picocolors';
22
import { ExecutorContext, logger, names } from '@nx/devkit';
33
import { signalToCode } from '@nx/devkit/internal';
44
import { ChildProcess, fork } from 'child_process';
5+
import { resolveExpoCliPath } from '../../utils/resolve-expo-cli';
56
import { resolve as pathResolve } from 'path';
67

78
import { ExpoStartOptions } from './schema';
@@ -47,7 +48,7 @@ function startAsync(
4748
): Promise<number> {
4849
return new Promise((resolve, reject) => {
4950
childProcess = fork(
50-
require.resolve('@expo/cli/build/bin/cli'),
51+
resolveExpoCliPath(),
5152
['start', ...createStartOptions(options)],
5253
{
5354
cwd: pathResolve(workspaceRoot, projectRoot),
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Resolve the path to the Expo CLI entry point.
3+
*
4+
* Expo SDK 55+ ships `@expo/cli` with an `exports` map where subpaths resolve
5+
* to `*.js` (`"./*": "./*.js"`). The CLI bin file is `build/bin/cli` (with no
6+
* extension), so `require.resolve('@expo/cli/build/bin/cli')` now fails with
7+
* MODULE_NOT_FOUND. The `expo` package's `bin/cli` is the stable entry point
8+
* (the same one `npx expo` uses) across all supported SDK versions, so prefer
9+
* it and fall back to the legacy `@expo/cli` path for older setups.
10+
*/
11+
export function resolveExpoCliPath(): string {
12+
try {
13+
return require.resolve('expo/bin/cli');
14+
} catch {
15+
return require.resolve('@expo/cli/build/bin/cli');
16+
}
17+
}

0 commit comments

Comments
 (0)