diff --git a/.changeset/fix-repack-init-npm-flow.md b/.changeset/fix-repack-init-npm-flow.md new file mode 100644 index 000000000..1055b2119 --- /dev/null +++ b/.changeset/fix-repack-init-npm-flow.md @@ -0,0 +1,5 @@ +--- +"@callstack/repack-init": patch +--- + +Fix npm-based project creation by using `npm exec ... -- rnc-cli` instead of a nested `npx` invocation, and pass a fully qualified React Native patch version to the React Native CLI. diff --git a/packages/init/src/tasks/createNewProject.ts b/packages/init/src/tasks/createNewProject.ts index a70e6bc33..d89a42bdc 100644 --- a/packages/init/src/tasks/createNewProject.ts +++ b/packages/init/src/tasks/createNewProject.ts @@ -5,6 +5,37 @@ import type { PackageManager } from '../types/pm.js'; import { RepackInitError } from '../utils/error.js'; import spinner from '../utils/spinner.js'; +function getCreateCommand(packageManager: PackageManager) { + if (packageManager.name === 'npm') { + // Use `npm exec` with the package's explicit bin name. Nested `npx` + // invocations can fail when repack-init itself is launched through `npx`. + return { + command: packageManager.runCommand, + args: [ + 'exec', + '--yes', + '--package', + '@react-native-community/cli@latest', + '--', + 'rnc-cli', + ], + shell: false, + }; + } + + return { + command: packageManager.dlxCommand, + args: ['@react-native-community/cli@latest'], + shell: true, + }; +} + +function getReactNativeVersion() { + const version = versionsJson['react-native']; + + return /^\d+\.\d+$/.test(version) ? `${version}.0` : version; +} + export default async function createNewProject( cwd: string, projectName: string, @@ -12,14 +43,15 @@ export default async function createNewProject( override: boolean ) { try { + const createCommand = getCreateCommand(packageManager); const args = [ - '@react-native-community/cli@latest', + ...createCommand.args, 'init', projectName, '--directory', path.join(cwd, projectName), '--version', - versionsJson['react-native'], + getReactNativeVersion(), '--skip-install', '--skip-git-init', ]; @@ -32,9 +64,9 @@ export default async function createNewProject( 'Creating new project from the React Native Community Template' ); - return await execa(packageManager.dlxCommand, args, { + return await execa(createCommand.command, args, { stdio: 'ignore', - shell: true, + shell: createCommand.shell, }); } catch { throw new RepackInitError( diff --git a/packages/init/tsconfig.build.json b/packages/init/tsconfig.build.json new file mode 100644 index 000000000..e63fac8ca --- /dev/null +++ b/packages/init/tsconfig.build.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/init/tsconfig.json b/packages/init/tsconfig.json index df59da578..9e25e6ece 100644 --- a/packages/init/tsconfig.json +++ b/packages/init/tsconfig.json @@ -1,8 +1,4 @@ { "extends": "../../tsconfig.base.json", - "compilerOptions": { - "rootDir": "src", - "outDir": "dist" - }, "include": ["src/**/*.ts"] }