nx-react-native-cli 2.5.3 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nx-react-native-cli",
3
- "version": "2.5.3",
3
+ "version": "2.6.0",
4
4
  "description": "A react native starter (with NX) cli script",
5
5
  "type": "module",
6
6
  "files": [
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "start": {
16
16
  "executor": "@nx/react-native:start",
17
- "dependsOn": ["check-env", "ensure-symlink", "pod-install"],
17
+ "dependsOn": ["check-env", "sync-deps", "pod-install"],
18
18
  "options": {
19
19
  "port": 8081,
20
20
  "resetCache": true,
@@ -29,12 +29,12 @@
29
29
  },
30
30
  "run-ios": {
31
31
  "executor": "@nx/react-native:run-ios",
32
- "dependsOn": ["ensure-symlink", "pod-install"],
32
+ "dependsOn": ["pod-install"],
33
33
  "options": {}
34
34
  },
35
35
  "bundle-ios": {
36
36
  "executor": "@nx/react-native:bundle",
37
- "dependsOn": ["ensure-symlink"],
37
+ "dependsOn": [],
38
38
  "outputs": ["{options.bundleOutput}"],
39
39
  "options": {
40
40
  "entryFile": "src/main.tsx",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "run-android": {
46
46
  "executor": "@nx/react-native:run-android",
47
- "dependsOn": ["ensure-symlink"],
47
+ "dependsOn": [],
48
48
  "options": {
49
49
  "mode": "debug",
50
50
  "tasks": "installDevDebug"
@@ -56,13 +56,13 @@
56
56
  "{projectRoot}/android/app/build/outputs/bundle",
57
57
  "{projectRoot}/android/app/build/outputs/apk"
58
58
  ],
59
- "dependsOn": ["ensure-symlink"],
59
+ "dependsOn": [],
60
60
  "options": {}
61
61
  },
62
62
  "build-ios": {
63
63
  "executor": "@nx/react-native:build-ios",
64
64
  "outputs": ["{projectRoot}/ios/build/Build"],
65
- "dependsOn": ["ensure-symlink", "pod-install"],
65
+ "dependsOn": [ "pod-install"],
66
66
  "options": {}
67
67
  },
68
68
  "pod-install": {
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "bundle-android": {
75
75
  "executor": "@nx/react-native:bundle",
76
- "dependsOn": ["ensure-symlink"],
76
+ "dependsOn": [],
77
77
  "outputs": ["{options.bundleOutput}"],
78
78
  "options": {
79
79
  "entryFile": "src/main.tsx",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "start": {
16
16
  "executor": "@nx/react-native:start",
17
- "dependsOn": ["check-env", "ensure-symlink", "pod-install"],
17
+ "dependsOn": ["check-env", "sync-deps", "pod-install"],
18
18
  "options": {
19
19
  "port": 8081,
20
20
  "resetCache": true,
@@ -29,12 +29,12 @@
29
29
  },
30
30
  "run-ios": {
31
31
  "executor": "@nx/react-native:run-ios",
32
- "dependsOn": ["ensure-symlink", "pod-install"],
32
+ "dependsOn": ["pod-install"],
33
33
  "options": {}
34
34
  },
35
35
  "bundle-ios": {
36
36
  "executor": "@nx/react-native:bundle",
37
- "dependsOn": ["ensure-symlink"],
37
+ "dependsOn": [],
38
38
  "outputs": ["{options.bundleOutput}"],
39
39
  "options": {
40
40
  "entryFile": "src/main.tsx",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "run-android": {
46
46
  "executor": "@nx/react-native:run-android",
47
- "dependsOn": ["ensure-symlink"],
47
+ "dependsOn": [],
48
48
  "options": {
49
49
  "mode": "debug",
50
50
  "tasks": "installDevDebug"
@@ -56,13 +56,13 @@
56
56
  "{projectRoot}/android/app/build/outputs/bundle",
57
57
  "{projectRoot}/android/app/build/outputs/apk"
58
58
  ],
59
- "dependsOn": ["ensure-symlink"],
59
+ "dependsOn": [],
60
60
  "options": {}
61
61
  },
62
62
  "build-ios": {
63
63
  "executor": "@nx/react-native:build-ios",
64
64
  "outputs": ["{projectRoot}/ios/build/Build"],
65
- "dependsOn": ["ensure-symlink", "pod-install"],
65
+ "dependsOn": [ "pod-install"],
66
66
  "options": {}
67
67
  },
68
68
  "pod-install": {
@@ -73,7 +73,7 @@
73
73
  },
74
74
  "bundle-android": {
75
75
  "executor": "@nx/react-native:bundle",
76
- "dependsOn": ["ensure-symlink"],
76
+ "dependsOn": [],
77
77
  "outputs": ["{options.bundleOutput}"],
78
78
  "options": {
79
79
  "entryFile": "src/main.tsx",
@@ -0,0 +1,356 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ require 'pathname'
5
+ require 'rexml/document'
6
+ require 'rexml/formatters/pretty'
7
+ require 'rexml/xpath'
8
+ require 'xcodeproj'
9
+
10
+ def ensure_build_configuration(container, config_name, base_config_name)
11
+ existing = container.build_configurations.find { |config| config.name == config_name }
12
+ return :existing if existing
13
+
14
+ base = container.build_configurations.find { |config| config.name == base_config_name }
15
+ raise "Base configuration '#{base_config_name}' was not found." unless base
16
+
17
+ duplicated = container.add_build_configuration(config_name, base.type)
18
+ duplicated.base_configuration_reference = base.base_configuration_reference
19
+ duplicated.build_settings = base.build_settings.dup
20
+ :created
21
+ end
22
+
23
+ def upsert_scheme_user_defined_setting(container)
24
+ updated = []
25
+ scheme_by_config = {
26
+ 'Dev.Debug' => 'Dev',
27
+ 'Dev.Release' => 'Dev',
28
+ 'Debug' => 'Prod',
29
+ 'Release' => 'Prod'
30
+ }
31
+ bundle_id_suffix_by_config = {
32
+ 'Dev.Debug' => '.dev',
33
+ 'Dev.Release' => '.dev',
34
+ 'Debug' => '',
35
+ 'Release' => ''
36
+ }
37
+
38
+ container.build_configurations.each do |config|
39
+ desired_scheme = scheme_by_config[config.name]
40
+ desired_suffix = bundle_id_suffix_by_config[config.name]
41
+ next unless desired_scheme
42
+
43
+ if config.build_settings['SCHEME'] != desired_scheme
44
+ config.build_settings['SCHEME'] = desired_scheme
45
+ updated << "#{container.respond_to?(:name) ? container.name : 'Project'} #{config.name} SCHEME=#{desired_scheme}"
46
+ end
47
+
48
+ current_suffix = config.build_settings['BUNDLE_ID_SUFFIX'].to_s
49
+ if current_suffix != desired_suffix
50
+ if desired_suffix.empty?
51
+ config.build_settings.delete('BUNDLE_ID_SUFFIX')
52
+ updated << "#{container.respond_to?(:name) ? container.name : 'Project'} #{config.name} BUNDLE_ID_SUFFIX=<removed>"
53
+ else
54
+ config.build_settings['BUNDLE_ID_SUFFIX'] = desired_suffix
55
+ updated << "#{container.respond_to?(:name) ? container.name : 'Project'} #{config.name} BUNDLE_ID_SUFFIX=#{desired_suffix}"
56
+ end
57
+ end
58
+ end
59
+
60
+ updated
61
+ end
62
+
63
+ def extract_base_bundle_identifier(app_target)
64
+ candidate = app_target.build_configurations.map { |config| config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'].to_s.strip }
65
+ .find { |value| !value.empty? && !value.include?('$(PRODUCT_NAME') }
66
+
67
+ return nil if candidate.nil? || candidate.empty?
68
+
69
+ candidate
70
+ .gsub('$(BUNDLE_ID_SUFFIX)', '')
71
+ .gsub(/\.dev\z/, '')
72
+ .strip
73
+ end
74
+
75
+ def upsert_product_bundle_identifier(app_target, base_bundle_identifier)
76
+ updated = []
77
+ desired_by_config = {
78
+ 'Dev.Debug' => "#{base_bundle_identifier}$(BUNDLE_ID_SUFFIX)",
79
+ 'Dev.Release' => "#{base_bundle_identifier}$(BUNDLE_ID_SUFFIX)",
80
+ 'Debug' => "#{base_bundle_identifier}$(BUNDLE_ID_SUFFIX)",
81
+ 'Release' => "#{base_bundle_identifier}$(BUNDLE_ID_SUFFIX)"
82
+ }
83
+
84
+ app_target.build_configurations.each do |config|
85
+ desired = desired_by_config[config.name]
86
+ next unless desired
87
+ next if config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] == desired
88
+
89
+ config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = desired
90
+ updated << "#{app_target.name} #{config.name} PRODUCT_BUNDLE_IDENTIFIER=#{desired}"
91
+ end
92
+
93
+ updated
94
+ end
95
+
96
+ def extract_base_product_name(app_target)
97
+ candidate = app_target.build_configurations
98
+ .map { |config| config.build_settings['PRODUCT_NAME'].to_s.strip }
99
+ .find { |value| !value.empty? && value != '$(TARGET_NAME)' }
100
+
101
+ return app_target.name if candidate.nil? || candidate.empty?
102
+
103
+ candidate.gsub(/\s*-\s*Dev\z/, '').strip
104
+ end
105
+
106
+ def upsert_product_name(app_target, base_product_name)
107
+ updated = []
108
+ desired_by_config = {
109
+ 'Dev.Debug' => "#{base_product_name} - Dev",
110
+ 'Dev.Release' => "#{base_product_name} - Dev",
111
+ 'Debug' => base_product_name,
112
+ 'Release' => base_product_name
113
+ }
114
+
115
+ app_target.build_configurations.each do |config|
116
+ desired = desired_by_config[config.name]
117
+ next unless desired
118
+ next if config.build_settings['PRODUCT_NAME'] == desired
119
+
120
+ config.build_settings['PRODUCT_NAME'] = desired
121
+ updated << "#{app_target.name} #{config.name} PRODUCT_NAME=#{desired}"
122
+ end
123
+
124
+ updated
125
+ end
126
+
127
+ def resolve_info_plist_paths(app_target, ios_dir, project_name)
128
+ app_target.build_configurations.map { |config| config.build_settings['INFOPLIST_FILE'].to_s.strip }
129
+ .reject(&:empty?)
130
+ .map do |value|
131
+ normalized = value.gsub('$(SRCROOT)', ios_dir).gsub('${SRCROOT}', ios_dir)
132
+ normalized = normalized.gsub('$(PROJECT_NAME)', project_name).gsub('${PROJECT_NAME}', project_name)
133
+ if Pathname.new(normalized).absolute?
134
+ normalized
135
+ else
136
+ File.expand_path(normalized, ios_dir)
137
+ end
138
+ end
139
+ .uniq
140
+ end
141
+
142
+ def upsert_info_plist_display_name(app_target, ios_dir, project_name)
143
+ paths = resolve_info_plist_paths(app_target, ios_dir, project_name)
144
+ updated = []
145
+
146
+ paths.each do |path|
147
+ next unless File.exist?(path)
148
+
149
+ plist = Xcodeproj::Plist.read_from_path(path) || {}
150
+ next if plist['CFBundleDisplayName'] == '$(PRODUCT_NAME)'
151
+
152
+ plist['CFBundleDisplayName'] = '$(PRODUCT_NAME)'
153
+ Xcodeproj::Plist.write_to_path(plist, path)
154
+ updated << "Info.plist CFBundleDisplayName=$(PRODUCT_NAME) at #{path}"
155
+ end
156
+
157
+ updated
158
+ end
159
+
160
+ def update_scheme_configuration(doc, action_name, build_configuration)
161
+ action = REXML::XPath.first(doc, "//#{action_name}")
162
+ raise "Missing #{action_name} in source scheme." unless action
163
+
164
+ action.attributes['buildConfiguration'] = build_configuration
165
+ end
166
+
167
+ def upsert_podfile_project_mapping(podfile_path, project_name)
168
+ return :missing unless File.exist?(podfile_path)
169
+
170
+ desired_block = <<~RUBY.chomp
171
+ project '#{project_name}',
172
+ 'Dev.Debug' => :debug,
173
+ 'Dev.Release' => :release,
174
+ 'Debug' => :debug,
175
+ 'Release' => :release
176
+ RUBY
177
+
178
+ content = File.read(podfile_path)
179
+ return :unchanged if content.include?(desired_block)
180
+
181
+ updated = content.dup
182
+ project_block_pattern = /project\s+'[^']+'\s*,\s*(?:\n\s*'[^']+'\s*=>\s*:\w+\s*,?)+/m
183
+
184
+ if updated.match?(project_block_pattern)
185
+ updated.sub!(project_block_pattern, desired_block)
186
+ elsif updated.include?('prepare_react_native_project!')
187
+ updated.sub!(/prepare_react_native_project!\s*\n+/) do
188
+ "prepare_react_native_project!\n\n#{desired_block}\n\n"
189
+ end
190
+ else
191
+ updated = "#{desired_block}\n\n#{updated}"
192
+ end
193
+
194
+ return :unchanged if updated == content
195
+
196
+ File.write(podfile_path, updated)
197
+ :updated
198
+ end
199
+
200
+ def upsert_firebase_environment_script_phase(app_target)
201
+ phase_name = 'Setup Firebase Environment GoogleService-Info.plist'
202
+ script_body = <<~'SCRIPT'.strip
203
+ # Name of the resource we're selectively copying
204
+ GOOGLESERVICE_INFO_PLIST=GoogleService-Info.plist
205
+ # Get references to dev and prod versions of the GoogleService-Info.plist
206
+ # NOTE: These should only live on the file system and should NOT be part of the target (since we'll be adding them to the target manually)
207
+ GOOGLESERVICE_INFO_DEV=${PROJECT_DIR}/Firebase/Dev/${GOOGLESERVICE_INFO_PLIST}
208
+ #GOOGLESERVICE_INFO_STAGING=${PROJECT_DIR}/Firebase/Staging/${GOOGLESERVICE_INFO_PLIST}
209
+ GOOGLESERVICE_INFO_PROD=${PROJECT_DIR}/Firebase/Prod/${GOOGLESERVICE_INFO_PLIST}
210
+ # Make sure the dev version of GoogleService-Info.plist exists
211
+ echo "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_DEV}"
212
+ if [ ! -f "${GOOGLESERVICE_INFO_DEV}" ]
213
+ then
214
+ echo "No Dev GoogleService-Info.plist found. Skipping Firebase plist setup."
215
+ exit 0
216
+ fi
217
+ # Make sure the staging version of GoogleService-Info.plist exists
218
+ # echo "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_STAGING}"
219
+ # if [ ! -f $GOOGLESERVICE_INFO_STAGING ]
220
+ # then
221
+ # echo "No Staging GoogleService-Info.plist found. Please ensure it's in the proper directory."
222
+ # exit 1
223
+ # fi
224
+ # Make sure the prod version of GoogleService-Info.plist exists
225
+ echo "Looking for ${GOOGLESERVICE_INFO_PLIST} in ${GOOGLESERVICE_INFO_PROD}"
226
+ if [ ! -f "${GOOGLESERVICE_INFO_PROD}" ]
227
+ then
228
+ echo "No Prod GoogleService-Info.plist found. Skipping Firebase plist setup."
229
+ exit 0
230
+ fi
231
+
232
+ # Log current user-defined configurations
233
+ #echo "CURRENT CONFIGURATION: ${CONFIGURATION}"
234
+ #echo "CURRENT SCHEME: ${SCHEME}"
235
+ #echo "CURRENT PRODUCT_BUNDLE_IDENTIFIER: ${PRODUCT_BUNDLE_IDENTIFIER}"
236
+ #echo "CURRENT BUNDLE_ID_SUFFIX: ${BUNDLE_ID_SUFFIX}"
237
+ #echo "CURRENT FACEBOOK_APP_ID: ${FACEBOOK_APP_ID}"
238
+ #echo "CURRENT FACEBOOK_CLIENT_TOKEN: ${FACEBOOK_CLIENT_TOKEN}"
239
+
240
+ # Get a reference to the destination location for the GoogleService-Info.plist
241
+ PLIST_DESTINATION=${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
242
+ echo "Will copy ${GOOGLESERVICE_INFO_PLIST} to final destination: ${PLIST_DESTINATION}"
243
+
244
+ # Copy over the prod GoogleService-Info.plist for Release builds
245
+ if [ "${SCHEME}" == "Dev" ]
246
+ then
247
+ echo "Using DEV ${GOOGLESERVICE_INFO_DEV}"
248
+ cp "${GOOGLESERVICE_INFO_DEV}" "${PLIST_DESTINATION}"
249
+ # elif [ "${SCHEME}" == "Staging" ]
250
+ # then
251
+ # echo "Using STAGING ${GOOGLESERVICE_INFO_STAGING}"
252
+ # cp "${GOOGLESERVICE_INFO_STAGING}" "${PLIST_DESTINATION}"
253
+ else
254
+ echo "Using PROD ${GOOGLESERVICE_INFO_PROD}"
255
+ cp "${GOOGLESERVICE_INFO_PROD}" "${PLIST_DESTINATION}"
256
+ fi
257
+ SCRIPT
258
+
259
+ existing_phase = app_target.build_phases.find do |phase|
260
+ phase.isa == 'PBXShellScriptBuildPhase' && phase.name == phase_name
261
+ end
262
+ phase_status = 'existing'
263
+ if existing_phase.nil?
264
+ existing_phase = app_target.new_shell_script_build_phase(phase_name)
265
+ phase_status = 'created'
266
+ end
267
+
268
+ if existing_phase.shell_script != script_body
269
+ existing_phase.shell_script = script_body
270
+ phase_status = phase_status == 'created' ? 'created' : 'updated'
271
+ end
272
+ existing_phase.shell_path = '/bin/bash'
273
+
274
+ resources_phase = app_target.build_phases.find do |phase|
275
+ phase.isa == 'PBXResourcesBuildPhase' ||
276
+ (phase.respond_to?(:display_name) && phase.display_name == 'Copy Bundle Resources') ||
277
+ (phase.respond_to?(:name) && phase.name == 'Copy Bundle Resources')
278
+ end
279
+
280
+ if resources_phase
281
+ app_target.build_phases.delete(existing_phase)
282
+ insert_index = app_target.build_phases.index(resources_phase) || app_target.build_phases.length
283
+ app_target.build_phases.insert(insert_index, existing_phase)
284
+ phase_status = phase_status == 'existing' ? 'reordered' : phase_status
285
+ end
286
+
287
+ phase_status
288
+ end
289
+
290
+ ios_dir = File.join(Dir.pwd, 'ios')
291
+ project_path = Dir.glob(File.join(ios_dir, '*.xcodeproj')).first
292
+ raise 'Could not find an .xcodeproj under ios/.' unless project_path
293
+
294
+ project = Xcodeproj::Project.open(project_path)
295
+ project_name = File.basename(project_path, '.xcodeproj')
296
+ app_target = project.targets.find do |target|
297
+ target.respond_to?(:product_type) && target.product_type == 'com.apple.product-type.application'
298
+ end
299
+ raise 'Could not find an iOS application target in the project.' unless app_target
300
+
301
+ changes = []
302
+
303
+ changes << 'project Dev.Debug' if ensure_build_configuration(project, 'Dev.Debug', 'Debug') == :created
304
+ changes << 'project Dev.Release' if ensure_build_configuration(project, 'Dev.Release', 'Release') == :created
305
+ changes.concat(upsert_scheme_user_defined_setting(project))
306
+
307
+ project.targets.each do |target|
308
+ changes << "#{target.name} Dev.Debug" if ensure_build_configuration(target, 'Dev.Debug', 'Debug') == :created
309
+ changes << "#{target.name} Dev.Release" if ensure_build_configuration(target, 'Dev.Release', 'Release') == :created
310
+ changes.concat(upsert_scheme_user_defined_setting(target))
311
+ end
312
+
313
+ base_bundle_identifier = extract_base_bundle_identifier(app_target)
314
+ raise 'Could not infer base PRODUCT_BUNDLE_IDENTIFIER from app target.' if base_bundle_identifier.nil? || base_bundle_identifier.empty?
315
+ changes.concat(upsert_product_bundle_identifier(app_target, base_bundle_identifier))
316
+ base_product_name = extract_base_product_name(app_target)
317
+ changes.concat(upsert_product_name(app_target, base_product_name))
318
+ changes.concat(upsert_info_plist_display_name(app_target, ios_dir, project_name))
319
+ firebase_phase_status = upsert_firebase_environment_script_phase(app_target)
320
+ changes << "Firebase plist run script #{firebase_phase_status}" unless firebase_phase_status == 'existing'
321
+
322
+ project.save
323
+
324
+ xcschemes_dir = File.join(project_path, 'xcshareddata', 'xcschemes')
325
+ FileUtils.mkdir_p(xcschemes_dir)
326
+
327
+ source_scheme_path = Dir.glob(File.join(xcschemes_dir, '*.xcscheme'))
328
+ .reject { |path| File.basename(path) == 'Dev.xcscheme' }
329
+ .first
330
+ raise 'Could not find a shared scheme to clone in ios/*.xcodeproj/xcshareddata/xcschemes.' unless source_scheme_path
331
+
332
+ scheme_doc = REXML::Document.new(File.read(source_scheme_path))
333
+ update_scheme_configuration(scheme_doc, 'LaunchAction', 'Dev.Debug')
334
+ update_scheme_configuration(scheme_doc, 'TestAction', 'Dev.Debug')
335
+ update_scheme_configuration(scheme_doc, 'AnalyzeAction', 'Dev.Debug')
336
+ update_scheme_configuration(scheme_doc, 'ProfileAction', 'Dev.Release')
337
+ update_scheme_configuration(scheme_doc, 'ArchiveAction', 'Dev.Release')
338
+
339
+ dev_scheme_path = File.join(xcschemes_dir, 'Dev.xcscheme')
340
+ output = +''
341
+ output << %(<?xml version="1.0" encoding="UTF-8"?>\n)
342
+ formatter = REXML::Formatters::Pretty.new(2)
343
+ formatter.compact = true
344
+ formatter.write(scheme_doc.root, output)
345
+ output << "\n"
346
+ File.write(dev_scheme_path, output)
347
+
348
+ podfile_status = upsert_podfile_project_mapping(File.join(ios_dir, 'Podfile'), project_name)
349
+ changes << 'Podfile project mapping' if podfile_status == :updated
350
+
351
+ puts 'Configured iOS build configurations and Dev scheme.'
352
+ if changes.empty?
353
+ puts 'No new build configurations were created (already configured).'
354
+ else
355
+ puts "Created: #{changes.join(', ')}"
356
+ end
@@ -64,7 +64,7 @@ export const focusedInputStyle = (isFocused: boolean) => isFocused && tw``;
64
64
 
65
65
  export const errorTextStyle = (err: boolean) => err && tw`text-red-600`;
66
66
 
67
- export const defaultInputContainerStyle = tw`flex-row rounded-xl border border-gray-900 p-3`;
67
+ export const defaultInputContainerStyle = tw`ios:p-3 android:p-1 flex-row rounded-xl border border-gray-900`;
68
68
  export const defaultInputTextStyle = tw`flex-1 font-normal text-gray-950`;
69
69
 
70
70
  export const colors = {