diff --git a/packages/react-native-builder-bob/src/targets/codegen.ts b/packages/react-native-builder-bob/src/targets/codegen.ts index ecb1c04b2..31d59a81b 100644 --- a/packages/react-native-builder-bob/src/targets/codegen.ts +++ b/packages/react-native-builder-bob/src/targets/codegen.ts @@ -1,10 +1,10 @@ import kleur from 'kleur'; import type { Input } from '../types'; import { patchCodegen } from '../utils/patchCodegen'; -import { spawn } from '../utils/spawn'; import fs from 'fs-extra'; import path from 'path'; import del from 'del'; +import { runRNCCli } from '../utils/runRNCCli'; type Options = Input; @@ -33,11 +33,9 @@ export default async function build({ root, report }: Options) { } try { - await spawn('npx', ['@react-native-community/cli', 'codegen'], { - stdio: 'ignore', - }); + await runRNCCli(['codegen']); - patchCodegen(root, packageJson, report); + await patchCodegen(root, packageJson, report); report.success('Generated native code with codegen'); } catch (e: unknown) { @@ -47,7 +45,17 @@ export default async function build({ root, report }: Options) { `Errors found while generating codegen files:\n${e.stdout.toString()}` ); } else if ('message' in e && typeof e.message === 'string') { - report.error(e.message); + if ( + e.message.includes( + "Error: Cannot find module '@react-native-community/cli/package.json'" + ) + ) { + report.error( + "You don't have `@react-native-community/cli` in your root package's dev dependencies. Please install it and make sure it uses the same version as your application." + ); + } else { + report.error(e.message); + } } else { throw e; } @@ -55,6 +63,6 @@ export default async function build({ root, report }: Options) { throw e; } - throw new Error('Failed generate the codegen files.'); + process.exit(1); } } diff --git a/packages/react-native-builder-bob/src/utils/runRNCCli.ts b/packages/react-native-builder-bob/src/utils/runRNCCli.ts new file mode 100644 index 000000000..744bf1cc8 --- /dev/null +++ b/packages/react-native-builder-bob/src/utils/runRNCCli.ts @@ -0,0 +1,54 @@ +import { type SpawnOptions } from 'node:child_process'; +import { spawn } from './spawn'; +import path from 'node:path'; +import fs from 'fs-extra'; +import assert from 'node:assert'; + +// This is a special case for calling bob from the XCode scripts +// XCode scripts don't have the node binary properly set +// We expose an env value for node instead. +const NODE_BINARY = process.env['NODE_BINARY'] || 'node'; + +/** + * Runs the React Native Community CLI with the specified arguments + */ +export async function runRNCCli( + args: string[], + options: SpawnOptions = { + stdio: 'ignore', + } +) { + const rncCliBinaryName = await getCliBinaryName(); + + const RNC_CLI_BINARY_PATH = path.resolve( + process.cwd(), // We are always expected to run in the library + 'node_modules', + '.bin', + rncCliBinaryName + ); + + return await spawn(NODE_BINARY, [RNC_CLI_BINARY_PATH, ...args], options); +} + +async function getCliBinaryName(): Promise { + const rncCliPackagePath = await spawn(NODE_BINARY, [ + '-e', + `console.log(require.resolve('@react-native-community/cli/package.json'))`, + ]); + + const rncCliPackage = await fs.readJson(rncCliPackagePath); + const binProperty = rncCliPackage.bin as Record; + assert( + typeof binProperty === 'object', + "React Native CLI doesn't specify proper binaries" + ); + + const binaries = Object.keys(binProperty); + const rncCliBinaryName = binaries[0] as string; + assert( + typeof rncCliBinaryName === 'string', + "React Native Community CLI doesn't have any binaries to run" + ); + + return rncCliBinaryName; +}