@@ -9,7 +9,6 @@ import log from './log';
99import kax from './kax' ;
1010import util from 'util' ;
1111import semver from 'semver' ;
12- import os from 'os' ;
1312
1413const ex = util . promisify ( exec ) ;
1514const sp = util . promisify ( spawn ) ;
@@ -89,7 +88,9 @@ export default class ReactNativeCli {
8988 }
9089 const initCmd = `init ${ projectName } ${ options . join ( ' ' ) } ` ;
9190
92- if ( semver . gte ( rnVersion , '0.77.0' ) ) {
91+ if ( semver . gte ( rnVersion , '0.81.0' ) ) {
92+ return execp ( `npx @react-native-community/cli@15.1.3 ${ initCmd } ` ) ;
93+ } else if ( semver . gte ( rnVersion , '0.77.0' ) ) {
9394 return execp ( `npx @react-native-community/cli@15.0.1 ${ initCmd } ` ) ;
9495 } else if ( semver . gte ( rnVersion , '0.60.0' ) ) {
9596 return execp (
@@ -123,28 +124,36 @@ export default class ReactNativeCli {
123124 const shouldUseCommunityCliForProject =
124125 await this . shouldUseCommunityCliForProject ( workingDir ) ;
125126
126- let bundleCommand : string ;
127- if ( shouldUseCommunityCliForProject ) {
128- bundleCommand = `npx @react-native-community/cli bundle \
127+ const bundleArgs = `\
129128${ entryFile ? `--entry-file=${ entryFile } ` : '' } \
130129${ dev ? '--dev=true' : '--dev=false' } \
131130${ platform ? `--platform=${ platform } ` : '' } \
132131${ bundleOutput ? `--bundle-output=${ bundleOutput } ` : '' } \
133132${ assetsDest ? `--assets-dest=${ assetsDest } ` : '' } \
134133${ sourceMapOutput ? `--sourcemap-output=${ sourceMapOutput } ` : '' } \
135134${ resetCache ? '--reset-cache' : '' } `;
135+
136+ let bundleCommand : string ;
137+ const execOptions : any = { cwd : workingDir } ;
138+ if ( shouldUseCommunityCliForProject ) {
139+ // RN >= 0.77: Use npx @react-native-community/cli (ships metro plugin via RN itself)
140+ bundleCommand = `npx @react-native-community/cli bundle ${ bundleArgs } ` ;
136141 } else {
137- bundleCommand = `${ this . binaryPath } bundle \
138- ${ entryFile ? `--entry-file=${ entryFile } ` : '' } \
139- ${ dev ? '--dev=true' : '--dev=false' } \
140- ${ platform ? `--platform=${ platform } ` : '' } \
141- ${ bundleOutput ? `--bundle-output=${ bundleOutput } ` : '' } \
142- ${ assetsDest ? `--assets-dest=${ assetsDest } ` : '' } \
143- ${ sourceMapOutput ? `--sourcemap-output=${ sourceMapOutput } ` : '' } \
144- ${ resetCache ? '--reset-cache' : '' } `;
142+ // RN < 0.77: Use the project-local react-native CLI from node_modules
143+ // The composite project's react-native dependency ships its own CLI version
144+ // that includes the bundle command (via cli-plugin-metro)
145+ const localCliBin = workingDir
146+ ? path . join ( workingDir , 'node_modules' , '.bin' , 'react-native' )
147+ : 'react-native' ;
148+ bundleCommand = `${ localCliBin } bundle ${ bundleArgs } ` ;
149+ // Older Metro versions use md4 hash which is unsupported in Node 17+ (OpenSSL 3)
150+ execOptions . env = {
151+ ...process . env ,
152+ NODE_OPTIONS : '--openssl-legacy-provider' ,
153+ } ;
145154 }
146155
147- await execp ( bundleCommand , { cwd : workingDir } ) ;
156+ await execp ( bundleCommand , execOptions ) ;
148157 if ( ! ( await fs . pathExists ( bundleOutput ) ) ) {
149158 // Under some circumstances, Metro bundler process might fail
150159 // with some logs, but exit the process with a non error status code.
@@ -209,17 +218,14 @@ ${resetCache ? '--reset-cache' : ''}`;
209218 stdio : 'inherit' ,
210219 } ) ;
211220 } else {
212- spawn (
213- path . join (
214- cwd ,
215- `node_modules/.bin/rnc-cli${ os . platform ( ) === 'win32' ? '.cmd' : '' } ` ,
216- ) ,
217- [ 'start' , ...args ] ,
218- {
219- cwd,
220- stdio : 'inherit' ,
221- } ,
222- ) ;
221+ // RN < 0.77: Use the project-local react-native CLI from node_modules
222+ // The project's react-native dependency ships its own CLI version
223+ // that includes the start command (via cli-plugin-metro)
224+ const localCliBin = path . join ( cwd , 'node_modules' , '.bin' , 'react-native' ) ;
225+ spawn ( localCliBin , [ 'start' , ...args ] , {
226+ cwd,
227+ stdio : 'inherit' ,
228+ } ) ;
223229 }
224230 }
225231
@@ -329,22 +335,32 @@ ${resetCache ? '--reset-cache' : ''}`;
329335 const tmpDir = createTmpDir ( ) ;
330336 const tmpScriptPath = path . join ( tmpDir , scriptFileName ) ;
331337
332- // Check if we should use @react -native-community/cli for RN 0.77+
333- let command = `${ this . binaryPath } start ${ args . join ( ' ' ) } ` ;
338+ // Determine which CLI to use based on RN version
339+ let command : string ;
340+ let useCommunityCliLatest = false ;
334341 try {
335342 const packageJsonPath = path . join ( cwd , 'package.json' ) ;
336343 if ( await fs . pathExists ( packageJsonPath ) ) {
337344 const packageJson = await fs . readJSON ( packageJsonPath ) ;
338345 const rnVersion =
339346 packageJson . dependencies ?. [ 'react-native' ] || '0.60.0' ;
340- if ( semver . gte ( rnVersion . replace ( / [ \^ ~ ] / , '' ) , '0.77.0' ) ) {
341- command = `npx @react-native-community/cli start ${ args . join ( ' ' ) } ` ;
342- }
347+ useCommunityCliLatest = semver . gte (
348+ rnVersion . replace ( / [ \^ ~ ] / , '' ) ,
349+ '0.77.0' ,
350+ ) ;
343351 }
344352 } catch ( e ) {
345353 // If we can't determine version, use legacy approach
346354 }
347355
356+ if ( useCommunityCliLatest ) {
357+ command = `npx @react-native-community/cli start ${ args . join ( ' ' ) } ` ;
358+ } else {
359+ // RN < 0.77: Use the project-local react-native CLI from node_modules
360+ const localCliBin = path . join ( cwd , 'node_modules' , '.bin' , 'react-native' ) ;
361+ command = `${ localCliBin } start ${ args . join ( ' ' ) } ` ;
362+ }
363+
348364 await fs . writeFile (
349365 tmpScriptPath ,
350366 `
0 commit comments