diff --git a/.changeset/podfile-patch-preserve-args.md b/.changeset/podfile-patch-preserve-args.md new file mode 100644 index 000000000..3c7e330eb --- /dev/null +++ b/.changeset/podfile-patch-preserve-args.md @@ -0,0 +1,5 @@ +--- +'create-rock': patch +--- + +fix(create-app): preserve existing arg list when patching Podfile diff --git a/packages/create-app/src/lib/utils/__tests__/initInExistingProject.test.ts b/packages/create-app/src/lib/utils/__tests__/initInExistingProject.test.ts index 8c3660113..d13291d64 100644 --- a/packages/create-app/src/lib/utils/__tests__/initInExistingProject.test.ts +++ b/packages/create-app/src/lib/utils/__tests__/initInExistingProject.test.ts @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import { cleanup, getTempDirectory, writeFiles } from '@rock-js/test-helpers'; import * as tools from '@rock-js/tools'; -import { updateAndroidBuildGradle } from '../initInExistingProject.js'; +import { updateAndroidBuildGradle, updatePodfile } from '../initInExistingProject.js'; const directory = getTempDirectory('test_updateAndroidBuildGradle'); @@ -120,3 +120,73 @@ describe('updateAndroidBuildGradle', () => { ); }); }); + +describe('updatePodfile', () => { + it('replaces a bare `use_native_modules!` call (Community-CLI template)', () => { + const content = ` +target 'App' do + config = use_native_modules! + + use_react_native!(:path => config[:reactNativePath]) +end +`; + const expected = ` +target 'App' do + config = use_native_modules!(['npx', 'rock', 'config', '-p', 'ios']) + + use_react_native!(:path => config[:reactNativePath]) +end +`; + writeFiles(directory, { 'ios/Podfile': content }); + updatePodfile(directory, 'ios'); + + expect( + fs.readFileSync(path.join(directory, 'ios/Podfile'), 'utf8'), + ).toStrictEqual(expected); + }); + + it('replaces a `use_native_modules!(config_command)` call (Expo prebuild template, regression test for #702)', () => { + const content = ` +target 'App' do + config_command = ['node', '--no-warnings', '--eval', "require('expo/bin/autolinking')"] + config = use_native_modules!(config_command) + + use_react_native!(:path => config[:reactNativePath]) +end +`; + const expected = ` +target 'App' do + config_command = ['node', '--no-warnings', '--eval', "require('expo/bin/autolinking')"] + config = use_native_modules!(['npx', 'rock', 'config', '-p', 'ios']) + + use_react_native!(:path => config[:reactNativePath]) +end +`; + writeFiles(directory, { 'ios/Podfile': content }); + updatePodfile(directory, 'ios'); + + expect( + fs.readFileSync(path.join(directory, 'ios/Podfile'), 'utf8'), + ).toStrictEqual(expected); + }); + + it('is idempotent — running on an already-patched Podfile is a no-op', () => { + const content = ` +target 'App' do + config = use_native_modules!(['npx', 'rock', 'config', '-p', 'ios']) +end +`; + writeFiles(directory, { 'ios/Podfile': content }); + updatePodfile(directory, 'ios'); + + expect( + fs.readFileSync(path.join(directory, 'ios/Podfile'), 'utf8'), + ).toStrictEqual(content); + }); + + it('does nothing when there is no Podfile', () => { + // No Podfile written; updatePodfile should return without throwing. + expect(() => updatePodfile(directory, 'ios')).not.toThrow(); + }); +}); + diff --git a/packages/create-app/src/lib/utils/initInExistingProject.ts b/packages/create-app/src/lib/utils/initInExistingProject.ts index 572d2771a..93d461af0 100644 --- a/packages/create-app/src/lib/utils/initInExistingProject.ts +++ b/packages/create-app/src/lib/utils/initInExistingProject.ts @@ -271,15 +271,21 @@ Please update the "Bundle React Native code and images" build phase manually wit ); } -function updatePodfile(projectRoot: string, sourceDir: string) { +export function updatePodfile(projectRoot: string, sourceDir: string) { const filePath = path.join(projectRoot, sourceDir, 'Podfile'); if (!fs.existsSync(filePath)) { return; } const content = fs.readFileSync(filePath, 'utf8'); + // Replace `config = use_native_modules!` together with any existing + // argument list. The Community-CLI Podfile template calls it with no + // arguments (`use_native_modules!`), but the Expo prebuild template + // passes its own autolinking command in (`use_native_modules!(config_command)`). + // Without consuming that argument the previous regex left it dangling + // on the line and produced invalid Ruby — see issue #702. const replaced = content.replace( - /(config\s*=\s*use_native_modules!)(\s*)/g, - "$1(['npx', 'rock', 'config', '-p', 'ios'])$2", + /(config\s*=\s*use_native_modules!)(\s*\([^)]*\))?/g, + "$1(['npx', 'rock', 'config', '-p', 'ios'])", ); if ( !content.includes(`(['npx', 'rock', 'config', '-p', 'ios'])`) &&