1+ # CocoaPods-based React Native Colo Loco
2+ # This version generates files for the IRNativeModules pod instead of directly linking to Xcode
3+ require 'fileutils'
4+ require 'pathname'
5+
6+ # Color constants for output
7+ if ENV [ 'NO_COLOR' ]
8+ R = "" # Red
9+ RB = "" # Red bold
10+ G = "" # Green
11+ GB = "" # Green bold
12+ BB = "" # Blue bold
13+ Y = "" # Yellow
14+ YB = "" # Yellow bold
15+ D = "" # Dark gray
16+ DD = "" # Darker gray
17+ DB = "" # Dark gray bold
18+ DDB = "" # Darker gray bold
19+ S = "" # Strikethrough
20+ X = "\033 [0m" # Reset
21+ elsif ENV [ 'PREFERS_CONTRAST' ] == 'more'
22+ R = "\033 [91m" # Bright red
23+ RB = "\033 [91m" # Bright red
24+ G = "\033 [92m" # Bright green
25+ GB = "\033 [92m" # Bright green
26+ BB = "\033 [94m" # Bright blue
27+ Y = "\033 [93m" # Bright yellow
28+ YB = "\033 [93m" # Bright yellow
29+ D = "\033 [37m" # White
30+ DD = "\033 [37m" # White
31+ DB = "\033 [37m" # White
32+ DDB = "\033 [37m" # White
33+ S = "\033 [9m" # Strikethrough
34+ X = "\033 [0m" # Reset
35+ else
36+ R = "\033 [31m" # Red
37+ RB = "\033 [31;1m" # Red bold
38+ G = "\033 [32m" # Green
39+ GB = "\033 [32;1m" # Green bold
40+ BB = "\033 [32;1m" # Blue bold
41+ Y = "\033 [33m" # Yellow
42+ YB = "\033 [33;1m" # Yellow bold
43+ D = "\033 [90m" # Dark gray
44+ DD = "\033 [90;2m" # Darker gray
45+ DB = "\033 [90;1m" # Dark gray bold
46+ DDB = "\033 [90;1;2m" # Darker gray bold
47+ S = "\033 [9m" # Strikethrough
48+ X = "\033 [0m" # Reset
49+ end
50+
51+ $changes_made = false
52+
53+ puts "#{ X } "
54+
55+ # This method will generate files for the IRNativeModules pod
56+ # instead of directly modifying the Xcode project
57+ def generate_native_files_for_pod ( options = { } )
58+ puts "🤪 #{ BB } React Native Colo Loco (CocoaPods)#{ X } "
59+ puts ""
60+ puts "#{ D } Running ./bin/generate_native_files.rb from #{ __FILE__ } #{ X } "
61+ puts ""
62+
63+ _verify_options! ( options )
64+
65+ app_path = options [ :app_path ]
66+ project_root = options [ :project_root ] || Pathname . pwd
67+ clean = options [ :clean ] || false
68+
69+ relative_app_path = Pathname . new ( app_path ) . relative_path_from ( project_root )
70+
71+ puts "#{ D } App Path: #{ BB } #{ relative_app_path } #{ X } "
72+ puts "#{ D } Project Root: #{ G } #{ project_root } #{ X } "
73+ puts "#{ D } clean: #{ G } #{ clean } #{ X } " if clean
74+ puts ""
75+
76+ return unless _check_app_path ( app_path )
77+
78+ generated_files_path = File . join ( project_root , 'macos' , 'build' , 'generated' , 'colocated' )
79+
80+ # if clean is true, remove the generated files
81+ if clean
82+ _clean_generated_files ( generated_files_path , project_root )
83+ return
84+ end
85+
86+ # Ensure the generated files directory exists
87+ FileUtils . mkdir_p ( generated_files_path )
88+
89+ # Run the ./generateTurboModule.js script to generate any embedded TurboModules
90+ _generate_turbomodules ( project_root )
91+
92+ # Get all colocated files in the app_path
93+ colocated_files = Dir . glob ( File . join ( app_path , '**/*.{h,m,mm,c,swift,cpp}' ) ) . map { |file | Pathname . new ( file ) . realpath }
94+
95+ puts "#{ D } Processing #{ colocated_files . length } native files for CocoaPods#{ X } "
96+ puts ""
97+
98+ # Generate headers for .mm files that don't have them
99+ colocated_files . each do |file |
100+ if file . extname == '.m' || file . extname == '.mm'
101+ # Check if there's already a header file
102+ header_file = file . sub_ext ( '.h' )
103+ next if colocated_files . any? { |f | f . basename == header_file . basename }
104+ next if file . basename . to_s . include? ( 'ComponentView' ) # Skip component views
105+
106+ _generate_objc_header ( file , generated_files_path , project_root )
107+ end
108+ end
109+
110+ # Also generate headers for any .mm files in the generated directory
111+ generated_mm_files = Dir . glob ( File . join ( generated_files_path , '*.mm' ) ) . map { |file | Pathname . new ( file ) . realpath }
112+ generated_mm_files . each do |file |
113+ # Check if header already exists
114+ header_file = file . sub_ext ( '.h' )
115+ next if File . exist? ( header_file )
116+
117+ _generate_objc_header ( file , generated_files_path , project_root )
118+ end
119+
120+ if $changes_made
121+ puts ""
122+ puts "#{ GB } ✓ Files generated for IRNativeModules pod#{ X } "
123+ puts "#{ D } Run 'pod install' to link the updated files to Xcode#{ X } "
124+ puts ""
125+ else
126+ puts ""
127+ puts "#{ D } No changes needed.#{ X } "
128+ puts ""
129+ end
130+ end
131+
132+ def _verify_options! ( options )
133+ if options [ :app_path ] . nil?
134+ raise "#{ RB } link_colocated_native_files_for_pod - You must specify an app_path#{ X } "
135+ end
136+ end
137+
138+ def _check_app_path ( app_path )
139+ return true if File . exist? ( app_path )
140+ puts "#{ RB } React Native Colo Loco error:#{ X } "
141+ puts ""
142+ puts "#{ Y } No files found in #{ BB } #{ app_path } #{ X } #{ Y } .#{ X } "
143+ puts "#{ Y } Please check your app_path.#{ X } "
144+ puts ""
145+ return false
146+ end
147+
148+ def _clean_generated_files ( generated_files_path , project_root )
149+ $changes_made = true
150+ relative_path = Pathname . new ( generated_files_path ) . relative_path_from ( project_root )
151+
152+ if File . exist? ( generated_files_path )
153+ FileUtils . rm_rf ( generated_files_path )
154+ puts "#{ YB } ➖ Removed #{ X } #{ S } #{ D } #{ relative_path } #{ X } #{ D } folder#{ X } "
155+ end
156+
157+ puts ""
158+ puts "#{ GB } ✓ Generated files cleaned#{ X } "
159+ puts ""
160+ end
161+
162+ def _generate_objc_header ( objc_file , generated_files_path , project_root )
163+ return if objc_file . extname != '.m' && objc_file . extname != '.mm'
164+
165+ template_header = File . read ( File . join ( File . dirname ( __FILE__ ) , 'templates' , 'MyTemplate.h' ) )
166+
167+ file_no_ext = objc_file . basename . sub_ext ( '' )
168+ header_file_path = File . join ( generated_files_path , "#{ file_no_ext } .h" )
169+
170+ # Don't regenerate if file exists and is newer than source
171+ if File . exist? ( header_file_path ) && File . mtime ( header_file_path ) > File . mtime ( objc_file )
172+ relative_header = Pathname . new ( header_file_path ) . relative_path_from ( project_root )
173+ puts "#{ DB } ✔️ Exists #{ X } #{ DB } #{ relative_header . basename } #{ X } #{ DD } #{ relative_header . dirname } #{ X } "
174+ return
175+ end
176+
177+ header_file_content = template_header . gsub ( "MyTemplate" , file_no_ext . to_s )
178+ FileUtils . mkdir_p ( File . dirname ( header_file_path ) )
179+ File . write ( header_file_path , header_file_content )
180+
181+ relative_header = Pathname . new ( header_file_path ) . relative_path_from ( project_root )
182+ puts "#{ BB } ➕ Generated #{ X } #{ BB } #{ relative_header . basename } #{ X } #{ DD } #{ relative_header . dirname } #{ X } "
183+ $changes_made = true
184+
185+ return Pathname . new ( header_file_path )
186+ end
187+
188+ def _generate_turbomodules ( project_root )
189+ puts "#{ D } Generating embedded TurboModules#{ X } "
190+ puts ""
191+ result = `node #{ project_root } /bin/generateTurboModule.js generate 2>&1`
192+ puts result
193+ puts ""
194+ $changes_made = true if result . include? ( "Generated" ) || result . include? ( "processed" )
195+ end
196+
197+ def _info_file_exists ( relative_path )
198+ return "#{ DB } ✔️ Exists #{ X } #{ DB } #{ relative_path . basename } #{ X } #{ DD } #{ relative_path . dirname } #{ X } "
199+ end
200+
201+ def _err_not_exists ( file )
202+ return "#{ YB } ⚠️ Warning #{ X } #{ D } #{ file } #{ X } #{ D } does not exist, skipping#{ X } "
203+ end
0 commit comments