@@ -7,7 +7,7 @@ import * as xcode from "@bacons/xcode";
77import * as xcodeJson from "@bacons/xcode/json" ;
88import * as zod from "zod" ;
99
10- import { chalk , spawn } from "@react-native-node-api/cli-utils" ;
10+ import { chalk , prettyPath , spawn } from "@react-native-node-api/cli-utils" ;
1111
1212import { getLibraryName } from "../path-utils.js" ;
1313import {
@@ -42,37 +42,73 @@ export async function ensureXcodeBuildPhase(fromPath: string) {
4242 target . props . productType === "com.apple.product-type.application" ,
4343 ) ;
4444
45+ let saveProject = false ;
46+
4547 for ( const target of applicationTargets ) {
4648 console . log ( `Patching '${ target . getDisplayName ( ) } ' target` ) ;
4749
4850 const existingBuildPhases = target . props . buildPhases . filter ( ( phase ) =>
4951 phase . getDisplayName ( ) . startsWith ( BUILD_PHASE_PREFIX ) ,
5052 ) ;
5153
54+ const shellScript = [
55+ "set -e" ,
56+ `'${ process . execPath } ' '${ CLI_PATH } ' link --apple '${ fromPath } '` ,
57+ ] . join ( "\n" ) ;
58+
59+ let foundBuildPhase = false ;
60+
5261 for ( const existingBuildPhase of existingBuildPhases ) {
53- console . log (
54- "Removing existing build phase:" ,
55- chalk . dim ( existingBuildPhase . getDisplayName ( ) ) ,
56- ) ;
57- existingBuildPhase . removeFromProject ( ) ;
62+ if (
63+ xcode . PBXShellScriptBuildPhase . is ( existingBuildPhase ) &&
64+ existingBuildPhase . getDisplayName ( ) === BUILD_PHASE_NAME
65+ ) {
66+ if ( existingBuildPhase . props . shellScript === shellScript ) {
67+ console . log ( "Found existing build phase with the same shell script" ) ;
68+ foundBuildPhase = true ;
69+ } else {
70+ console . log (
71+ "Updating existing build phase with the new shell script" ,
72+ ) ;
73+ existingBuildPhase . props . shellScript = shellScript ;
74+ foundBuildPhase = true ;
75+ saveProject = true ;
76+ }
77+ } else {
78+ // We're expecting no other build phases with this prefix
79+ console . log (
80+ "Removing existing build phase:" ,
81+ chalk . dim ( existingBuildPhase . getDisplayName ( ) ) ,
82+ ) ;
83+ existingBuildPhase . removeFromProject ( ) ;
84+ }
5885 }
5986
60- // TODO: Declare input and output files to prevent unnecessary runs
61-
62- target . createBuildPhase ( xcode . PBXShellScriptBuildPhase , {
63- name : BUILD_PHASE_NAME ,
64- shellScript : [
65- "set -e" ,
66- `' ${ process . execPath } ' ' ${ CLI_PATH } ' link --apple ' ${ fromPath } '` ,
67- ] . join ( "\n" ) ,
68- } ) ;
87+ if ( ! foundBuildPhase ) {
88+ console . log ( "Adding new build phase" ) ;
89+ // TODO: Consider declaring input and output files to prevent unnecessary runs
90+ target . createBuildPhase ( xcode . PBXShellScriptBuildPhase , {
91+ name : BUILD_PHASE_NAME ,
92+ shellScript ,
93+ } ) ;
94+ saveProject = true ;
95+ }
6996 }
7097
71- await fs . promises . writeFile (
72- pbxprojPath ,
73- xcodeJson . build ( xcodeProject . toJSON ( ) ) ,
74- "utf8" ,
75- ) ;
98+ if ( saveProject ) {
99+ console . log ( "Saving Xcode project" , prettyPath ( pbxprojPath ) ) ;
100+ await fs . promises . writeFile (
101+ pbxprojPath ,
102+ xcodeJson . build ( xcodeProject . toJSON ( ) ) ,
103+ "utf8" ,
104+ ) ;
105+ } else {
106+ console . log (
107+ "Skipped saving Xcode project" ,
108+ prettyPath ( pbxprojPath ) ,
109+ chalk . dim ( "(no changes were made)" ) ,
110+ ) ;
111+ }
76112}
77113
78114/**
0 commit comments