@callstack/react-native-brownfield 2.2.0 → 3.0.0-rc.2

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.
Files changed (230) hide show
  1. package/ReactBrownfield.podspec +4 -0
  2. package/ios/BrownfieldBundlePathResolver.swift +21 -0
  3. package/ios/ExpoHostRuntime.swift +140 -0
  4. package/ios/JSBundleLoadObserver.swift +38 -0
  5. package/ios/Notification+Brownfield.swift +12 -0
  6. package/ios/ReactNativeBrownfield.swift +78 -112
  7. package/ios/ReactNativeHostRuntime.swift +138 -0
  8. package/lib/commonjs/expo-config-plugin/android/constants.js +2 -0
  9. package/lib/commonjs/expo-config-plugin/android/constants.js.map +1 -0
  10. package/lib/commonjs/expo-config-plugin/android/gradleHelpers.js +2 -0
  11. package/lib/commonjs/expo-config-plugin/android/gradleHelpers.js.map +1 -0
  12. package/lib/commonjs/expo-config-plugin/android/index.js +2 -0
  13. package/lib/commonjs/expo-config-plugin/android/index.js.map +1 -0
  14. package/lib/commonjs/expo-config-plugin/android/withAndroidModuleFiles.js +2 -0
  15. package/lib/commonjs/expo-config-plugin/android/withAndroidModuleFiles.js.map +1 -0
  16. package/lib/commonjs/expo-config-plugin/android/withBrownfieldAndroid.js +2 -0
  17. package/lib/commonjs/expo-config-plugin/android/withBrownfieldAndroid.js.map +1 -0
  18. package/lib/commonjs/expo-config-plugin/app.plugin.js +2 -0
  19. package/lib/commonjs/expo-config-plugin/app.plugin.js.map +1 -0
  20. package/lib/commonjs/expo-config-plugin/errors/SourceModificationError.js +2 -0
  21. package/lib/commonjs/expo-config-plugin/errors/SourceModificationError.js.map +1 -0
  22. package/lib/commonjs/expo-config-plugin/index.js +2 -0
  23. package/lib/commonjs/expo-config-plugin/index.js.map +1 -0
  24. package/lib/commonjs/expo-config-plugin/ios/index.js +2 -0
  25. package/lib/commonjs/expo-config-plugin/ios/index.js.map +1 -0
  26. package/lib/commonjs/expo-config-plugin/ios/podfileHelpers.js +8 -0
  27. package/lib/commonjs/expo-config-plugin/ios/podfileHelpers.js.map +1 -0
  28. package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js +2 -0
  29. package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -0
  30. package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js +2 -0
  31. package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -0
  32. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js +2 -0
  33. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js.map +1 -0
  34. package/lib/commonjs/expo-config-plugin/logging.js +2 -0
  35. package/lib/commonjs/expo-config-plugin/logging.js.map +1 -0
  36. package/lib/commonjs/expo-config-plugin/template/android/AndroidManifest.xml +4 -0
  37. package/lib/commonjs/expo-config-plugin/template/android/ReactNativeHostManager.kt +54 -0
  38. package/lib/commonjs/expo-config-plugin/template/android/build.gradle.kts +90 -0
  39. package/lib/commonjs/expo-config-plugin/template/android/consumer-rules.pro +0 -0
  40. package/lib/commonjs/expo-config-plugin/template/android/gradle.properties +8 -0
  41. package/lib/commonjs/expo-config-plugin/template/android/proguard-rules.pro +21 -0
  42. package/lib/commonjs/expo-config-plugin/template/engine.js +2 -0
  43. package/lib/commonjs/expo-config-plugin/template/engine.js.map +1 -0
  44. package/lib/commonjs/expo-config-plugin/template/ios/FrameworkInterface.swift +13 -0
  45. package/lib/commonjs/expo-config-plugin/template/ios/Info.plist +22 -0
  46. package/lib/commonjs/expo-config-plugin/template/ios/PodfileTargetBlock.rb +5 -0
  47. package/lib/commonjs/expo-config-plugin/template/ios/patchExpoPre55.sh +20 -0
  48. package/lib/commonjs/expo-config-plugin/types/BrownfieldPluginConfig.js +2 -0
  49. package/lib/commonjs/expo-config-plugin/types/BrownfieldPluginConfig.js.map +1 -0
  50. package/lib/commonjs/expo-config-plugin/types/RenderedTemplateFile.js +2 -0
  51. package/lib/commonjs/expo-config-plugin/types/RenderedTemplateFile.js.map +1 -0
  52. package/lib/commonjs/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.js +2 -0
  53. package/lib/commonjs/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.js.map +1 -0
  54. package/lib/commonjs/expo-config-plugin/types/index.js +2 -0
  55. package/lib/commonjs/expo-config-plugin/types/index.js.map +1 -0
  56. package/lib/commonjs/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.js +2 -0
  57. package/lib/commonjs/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.js.map +1 -0
  58. package/lib/commonjs/expo-config-plugin/withBrownfield.js +2 -0
  59. package/lib/commonjs/expo-config-plugin/withBrownfield.js.map +1 -0
  60. package/lib/commonjs/scripts/brownfield.js +0 -0
  61. package/lib/module/expo-config-plugin/android/constants.js +2 -0
  62. package/lib/module/expo-config-plugin/android/constants.js.map +1 -0
  63. package/lib/module/expo-config-plugin/android/gradleHelpers.js +2 -0
  64. package/lib/module/expo-config-plugin/android/gradleHelpers.js.map +1 -0
  65. package/lib/module/expo-config-plugin/android/index.js +2 -0
  66. package/lib/module/expo-config-plugin/android/index.js.map +1 -0
  67. package/lib/module/expo-config-plugin/android/withAndroidModuleFiles.js +2 -0
  68. package/lib/module/expo-config-plugin/android/withAndroidModuleFiles.js.map +1 -0
  69. package/lib/module/expo-config-plugin/android/withBrownfieldAndroid.js +2 -0
  70. package/lib/module/expo-config-plugin/android/withBrownfieldAndroid.js.map +1 -0
  71. package/lib/module/expo-config-plugin/app.plugin.js +2 -0
  72. package/lib/module/expo-config-plugin/app.plugin.js.map +1 -0
  73. package/lib/module/expo-config-plugin/errors/SourceModificationError.js +2 -0
  74. package/lib/module/expo-config-plugin/errors/SourceModificationError.js.map +1 -0
  75. package/lib/module/expo-config-plugin/index.js +2 -0
  76. package/lib/module/expo-config-plugin/index.js.map +1 -0
  77. package/lib/module/expo-config-plugin/ios/index.js +2 -0
  78. package/lib/module/expo-config-plugin/ios/index.js.map +1 -0
  79. package/lib/module/expo-config-plugin/ios/podfileHelpers.js +8 -0
  80. package/lib/module/expo-config-plugin/ios/podfileHelpers.js.map +1 -0
  81. package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js +2 -0
  82. package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -0
  83. package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js +2 -0
  84. package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -0
  85. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js +2 -0
  86. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js.map +1 -0
  87. package/lib/module/expo-config-plugin/logging.js +2 -0
  88. package/lib/module/expo-config-plugin/logging.js.map +1 -0
  89. package/lib/module/expo-config-plugin/template/android/AndroidManifest.xml +4 -0
  90. package/lib/module/expo-config-plugin/template/android/ReactNativeHostManager.kt +54 -0
  91. package/lib/module/expo-config-plugin/template/android/build.gradle.kts +90 -0
  92. package/lib/module/expo-config-plugin/template/android/consumer-rules.pro +0 -0
  93. package/lib/module/expo-config-plugin/template/android/gradle.properties +8 -0
  94. package/lib/module/expo-config-plugin/template/android/proguard-rules.pro +21 -0
  95. package/lib/module/expo-config-plugin/template/engine.js +2 -0
  96. package/lib/module/expo-config-plugin/template/engine.js.map +1 -0
  97. package/lib/module/expo-config-plugin/template/ios/FrameworkInterface.swift +13 -0
  98. package/lib/module/expo-config-plugin/template/ios/Info.plist +22 -0
  99. package/lib/module/expo-config-plugin/template/ios/PodfileTargetBlock.rb +5 -0
  100. package/lib/module/expo-config-plugin/template/ios/patchExpoPre55.sh +20 -0
  101. package/lib/module/expo-config-plugin/types/BrownfieldPluginConfig.js +2 -0
  102. package/lib/module/expo-config-plugin/types/BrownfieldPluginConfig.js.map +1 -0
  103. package/lib/module/expo-config-plugin/types/RenderedTemplateFile.js +2 -0
  104. package/lib/module/expo-config-plugin/types/RenderedTemplateFile.js.map +1 -0
  105. package/lib/module/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.js +2 -0
  106. package/lib/module/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.js.map +1 -0
  107. package/lib/module/expo-config-plugin/types/index.js +2 -0
  108. package/lib/module/expo-config-plugin/types/index.js.map +1 -0
  109. package/lib/module/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.js +2 -0
  110. package/lib/module/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.js.map +1 -0
  111. package/lib/module/expo-config-plugin/withBrownfield.js +2 -0
  112. package/lib/module/expo-config-plugin/withBrownfield.js.map +1 -0
  113. package/lib/typescript/commonjs/src/expo-config-plugin/android/constants.d.ts +3 -0
  114. package/lib/typescript/commonjs/src/expo-config-plugin/android/constants.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/src/expo-config-plugin/android/gradleHelpers.d.ts +14 -0
  116. package/lib/typescript/commonjs/src/expo-config-plugin/android/gradleHelpers.d.ts.map +1 -0
  117. package/lib/typescript/commonjs/src/expo-config-plugin/android/index.d.ts +4 -0
  118. package/lib/typescript/commonjs/src/expo-config-plugin/android/index.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts +24 -0
  120. package/lib/typescript/commonjs/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/src/expo-config-plugin/android/withBrownfieldAndroid.d.ts +13 -0
  122. package/lib/typescript/commonjs/src/expo-config-plugin/android/withBrownfieldAndroid.d.ts.map +1 -0
  123. package/lib/typescript/commonjs/src/expo-config-plugin/app.plugin.d.ts +9 -0
  124. package/lib/typescript/commonjs/src/expo-config-plugin/app.plugin.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/src/expo-config-plugin/errors/SourceModificationError.d.ts +3 -0
  126. package/lib/typescript/commonjs/src/expo-config-plugin/errors/SourceModificationError.d.ts.map +1 -0
  127. package/lib/typescript/commonjs/src/expo-config-plugin/index.d.ts +7 -0
  128. package/lib/typescript/commonjs/src/expo-config-plugin/index.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/src/expo-config-plugin/ios/index.d.ts +5 -0
  130. package/lib/typescript/commonjs/src/expo-config-plugin/ios/index.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/src/expo-config-plugin/ios/podfileHelpers.d.ts +8 -0
  132. package/lib/typescript/commonjs/src/expo-config-plugin/ios/podfileHelpers.d.ts.map +1 -0
  133. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withBrownfieldIos.d.ts +14 -0
  134. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts +19 -0
  136. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts +33 -0
  138. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/src/expo-config-plugin/logging.d.ts +9 -0
  140. package/lib/typescript/commonjs/src/expo-config-plugin/logging.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/src/expo-config-plugin/template/engine.d.ts +9 -0
  142. package/lib/typescript/commonjs/src/expo-config-plugin/template/engine.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/src/expo-config-plugin/types/BrownfieldPluginConfig.d.ts +53 -0
  144. package/lib/typescript/commonjs/src/expo-config-plugin/types/BrownfieldPluginConfig.d.ts.map +1 -0
  145. package/lib/typescript/commonjs/src/expo-config-plugin/types/RenderedTemplateFile.d.ts +5 -0
  146. package/lib/typescript/commonjs/src/expo-config-plugin/types/RenderedTemplateFile.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/src/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.d.ts +50 -0
  148. package/lib/typescript/commonjs/src/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.d.ts.map +1 -0
  149. package/lib/typescript/commonjs/src/expo-config-plugin/types/index.d.ts +5 -0
  150. package/lib/typescript/commonjs/src/expo-config-plugin/types/index.d.ts.map +1 -0
  151. package/lib/typescript/commonjs/src/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.d.ts +36 -0
  152. package/lib/typescript/commonjs/src/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.d.ts.map +1 -0
  153. package/lib/typescript/commonjs/src/expo-config-plugin/withBrownfield.d.ts +5 -0
  154. package/lib/typescript/commonjs/src/expo-config-plugin/withBrownfield.d.ts.map +1 -0
  155. package/lib/typescript/module/src/expo-config-plugin/android/constants.d.ts +3 -0
  156. package/lib/typescript/module/src/expo-config-plugin/android/constants.d.ts.map +1 -0
  157. package/lib/typescript/module/src/expo-config-plugin/android/gradleHelpers.d.ts +14 -0
  158. package/lib/typescript/module/src/expo-config-plugin/android/gradleHelpers.d.ts.map +1 -0
  159. package/lib/typescript/module/src/expo-config-plugin/android/index.d.ts +4 -0
  160. package/lib/typescript/module/src/expo-config-plugin/android/index.d.ts.map +1 -0
  161. package/lib/typescript/module/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts +24 -0
  162. package/lib/typescript/module/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts.map +1 -0
  163. package/lib/typescript/module/src/expo-config-plugin/android/withBrownfieldAndroid.d.ts +13 -0
  164. package/lib/typescript/module/src/expo-config-plugin/android/withBrownfieldAndroid.d.ts.map +1 -0
  165. package/lib/typescript/module/src/expo-config-plugin/app.plugin.d.ts +9 -0
  166. package/lib/typescript/module/src/expo-config-plugin/app.plugin.d.ts.map +1 -0
  167. package/lib/typescript/module/src/expo-config-plugin/errors/SourceModificationError.d.ts +3 -0
  168. package/lib/typescript/module/src/expo-config-plugin/errors/SourceModificationError.d.ts.map +1 -0
  169. package/lib/typescript/module/src/expo-config-plugin/index.d.ts +7 -0
  170. package/lib/typescript/module/src/expo-config-plugin/index.d.ts.map +1 -0
  171. package/lib/typescript/module/src/expo-config-plugin/ios/index.d.ts +5 -0
  172. package/lib/typescript/module/src/expo-config-plugin/ios/index.d.ts.map +1 -0
  173. package/lib/typescript/module/src/expo-config-plugin/ios/podfileHelpers.d.ts +8 -0
  174. package/lib/typescript/module/src/expo-config-plugin/ios/podfileHelpers.d.ts.map +1 -0
  175. package/lib/typescript/module/src/expo-config-plugin/ios/withBrownfieldIos.d.ts +14 -0
  176. package/lib/typescript/module/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -0
  177. package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts +19 -0
  178. package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -0
  179. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts +33 -0
  180. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -0
  181. package/lib/typescript/module/src/expo-config-plugin/logging.d.ts +9 -0
  182. package/lib/typescript/module/src/expo-config-plugin/logging.d.ts.map +1 -0
  183. package/lib/typescript/module/src/expo-config-plugin/template/engine.d.ts +9 -0
  184. package/lib/typescript/module/src/expo-config-plugin/template/engine.d.ts.map +1 -0
  185. package/lib/typescript/module/src/expo-config-plugin/types/BrownfieldPluginConfig.d.ts +53 -0
  186. package/lib/typescript/module/src/expo-config-plugin/types/BrownfieldPluginConfig.d.ts.map +1 -0
  187. package/lib/typescript/module/src/expo-config-plugin/types/RenderedTemplateFile.d.ts +5 -0
  188. package/lib/typescript/module/src/expo-config-plugin/types/RenderedTemplateFile.d.ts.map +1 -0
  189. package/lib/typescript/module/src/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.d.ts +50 -0
  190. package/lib/typescript/module/src/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.d.ts.map +1 -0
  191. package/lib/typescript/module/src/expo-config-plugin/types/index.d.ts +5 -0
  192. package/lib/typescript/module/src/expo-config-plugin/types/index.d.ts.map +1 -0
  193. package/lib/typescript/module/src/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.d.ts +36 -0
  194. package/lib/typescript/module/src/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.d.ts.map +1 -0
  195. package/lib/typescript/module/src/expo-config-plugin/withBrownfield.d.ts +5 -0
  196. package/lib/typescript/module/src/expo-config-plugin/withBrownfield.d.ts.map +1 -0
  197. package/package.json +20 -7
  198. package/scripts/react_native_brownfield_post_integrate.rb +22 -0
  199. package/src/expo-config-plugin/android/constants.ts +2 -0
  200. package/src/expo-config-plugin/android/gradleHelpers.ts +72 -0
  201. package/src/expo-config-plugin/android/index.ts +6 -0
  202. package/src/expo-config-plugin/android/withAndroidModuleFiles.ts +139 -0
  203. package/src/expo-config-plugin/android/withBrownfieldAndroid.ts +48 -0
  204. package/src/expo-config-plugin/app.plugin.ts +10 -0
  205. package/src/expo-config-plugin/errors/SourceModificationError.ts +1 -0
  206. package/src/expo-config-plugin/index.ts +8 -0
  207. package/src/expo-config-plugin/ios/index.ts +7 -0
  208. package/src/expo-config-plugin/ios/podfileHelpers.ts +107 -0
  209. package/src/expo-config-plugin/ios/withBrownfieldIos.ts +94 -0
  210. package/src/expo-config-plugin/ios/withIosFrameworkFiles.ts +91 -0
  211. package/src/expo-config-plugin/ios/xcodeHelpers.ts +433 -0
  212. package/src/expo-config-plugin/logging.ts +29 -0
  213. package/src/expo-config-plugin/template/android/AndroidManifest.xml +4 -0
  214. package/src/expo-config-plugin/template/android/ReactNativeHostManager.kt +54 -0
  215. package/src/expo-config-plugin/template/android/build.gradle.kts +90 -0
  216. package/src/expo-config-plugin/template/android/consumer-rules.pro +0 -0
  217. package/src/expo-config-plugin/template/android/gradle.properties +8 -0
  218. package/src/expo-config-plugin/template/android/proguard-rules.pro +21 -0
  219. package/src/expo-config-plugin/template/engine.ts +26 -0
  220. package/src/expo-config-plugin/template/ios/FrameworkInterface.swift +13 -0
  221. package/src/expo-config-plugin/template/ios/Info.plist +22 -0
  222. package/src/expo-config-plugin/template/ios/PodfileTargetBlock.rb +5 -0
  223. package/src/expo-config-plugin/template/ios/patchExpoPre55.sh +20 -0
  224. package/src/expo-config-plugin/types/BrownfieldPluginConfig.ts +67 -0
  225. package/src/expo-config-plugin/types/RenderedTemplateFile.ts +4 -0
  226. package/src/expo-config-plugin/types/android/BrownfieldPluginAndroidConfig.ts +58 -0
  227. package/src/expo-config-plugin/types/index.ts +6 -0
  228. package/src/expo-config-plugin/types/ios/BrownfieldPluginIosConfig.ts +41 -0
  229. package/src/expo-config-plugin/withBrownfield.ts +112 -0
  230. package/CHANGELOG.md +0 -41
@@ -0,0 +1,433 @@
1
+ import path from 'node:path';
2
+
3
+ import {
4
+ type ModProps,
5
+ type XcodeProject,
6
+ IOSConfig,
7
+ } from '@expo/config-plugins';
8
+
9
+ import { Logger } from '../logging';
10
+ import type { ResolvedBrownfieldPluginIosConfig } from '../types';
11
+ import { SourceModificationError } from '../errors/SourceModificationError';
12
+ import { getFrameworkSourceFiles } from './withIosFrameworkFiles';
13
+ import { renderTemplate } from '../template/engine';
14
+
15
+ /**
16
+ * Adds a new Framework target to the Xcode project for Brownfield packaging
17
+ * @throws If target creation fails
18
+ * @param project The Xcode project to modify
19
+ * @param options Framework target options
20
+ */
21
+ export function addFrameworkTarget(
22
+ project: XcodeProject,
23
+ modRequest: ModProps<XcodeProject>,
24
+ options: ResolvedBrownfieldPluginIosConfig
25
+ ): {
26
+ frameworkTargetUUID: string;
27
+ targetAlreadyExists: boolean;
28
+ } {
29
+ const { frameworkName, bundleIdentifier } = options;
30
+
31
+ // check if target already exists
32
+ const existingTarget = project.pbxTargetByName(frameworkName);
33
+ if (existingTarget) {
34
+ Logger.logDebug(
35
+ `Framework target "${frameworkName}" already exists, skipping creation`
36
+ );
37
+
38
+ const frameworkTargetUUID = Object.entries(
39
+ project.pbxNativeTargetSection()
40
+ ).find(
41
+ ([_key, value]) =>
42
+ (value as any)?.productReference === existingTarget.productReference
43
+ )?.[0];
44
+
45
+ if (!frameworkTargetUUID) {
46
+ throw new SourceModificationError(
47
+ `Failed to find framework target UUID for ${frameworkName}, although it can be resolved by name`
48
+ );
49
+ }
50
+
51
+ return {
52
+ frameworkTargetUUID,
53
+ targetAlreadyExists: true,
54
+ };
55
+ }
56
+
57
+ Logger.logDebug(`Adding iOS framework target: ${frameworkName}`);
58
+
59
+ // create the framework target using 'framework' target type
60
+ const frameworkTarget = project.addTarget(
61
+ frameworkName,
62
+ 'framework',
63
+ frameworkName,
64
+ bundleIdentifier
65
+ );
66
+
67
+ if (!frameworkTarget) {
68
+ throw new SourceModificationError(
69
+ `Failed to create framework target: ${frameworkName}`
70
+ );
71
+ }
72
+
73
+ // get the target UUID for later use
74
+ // const targetUuid = frameworkTarget.uuid;
75
+ const frameworkBuildConfigurations =
76
+ project.pbxXCConfigurationList()[
77
+ frameworkTarget.pbxNativeTarget.buildConfigurationList
78
+ ];
79
+ const debugFrameworkConfigKey: string =
80
+ frameworkBuildConfigurations.buildConfigurations.find(
81
+ ({ comment }: { comment: string }) => comment === 'Debug'
82
+ ).value;
83
+ const releaseFrameworkConfigKey: string =
84
+ frameworkBuildConfigurations.buildConfigurations.find(
85
+ ({ comment }: { comment: string }) => comment === 'Release'
86
+ ).value;
87
+
88
+ // update build settings on the existing configuration list
89
+ const debugSettings = getFrameworkBuildSettings(
90
+ {
91
+ configuration: 'Debug',
92
+ },
93
+ options
94
+ );
95
+ const releaseSettings = getFrameworkBuildSettings(
96
+ {
97
+ configuration: 'Release',
98
+ },
99
+ options
100
+ );
101
+
102
+ var configs = project.pbxXCBuildConfigurationSection();
103
+
104
+ // look for existing configs for the framework target
105
+ for (const configName in configs) {
106
+ let sourceBuildSettings =
107
+ configName === releaseFrameworkConfigKey
108
+ ? releaseSettings
109
+ : configName === debugFrameworkConfigKey
110
+ ? debugSettings
111
+ : null;
112
+
113
+ // if we have matching settings, apply them
114
+ if (sourceBuildSettings) {
115
+ const destinationBuildSettings = configs[configName].buildSettings;
116
+ for (const key in sourceBuildSettings) {
117
+ destinationBuildSettings[key] = sourceBuildSettings[key];
118
+ }
119
+
120
+ Logger.logDebug(
121
+ `Updated build settings for ${configName} configuration of target ${frameworkName}`
122
+ );
123
+ }
124
+ }
125
+
126
+ // Update build settings for the target
127
+ Object.entries(debugSettings).forEach(([key, value]) => {
128
+ project.updateBuildProperty(key, value, 'Debug', frameworkName);
129
+ });
130
+ Object.entries(releaseSettings).forEach(([key, value]) => {
131
+ project.updateBuildProperty(key, value, 'Release', frameworkName);
132
+ });
133
+
134
+ // create the framework group in the project
135
+ const filePaths = getFrameworkSourceFiles(options).map(
136
+ (file) => file.relativePath
137
+ );
138
+ const groupPath = path.join(modRequest.platformProjectRoot, frameworkName);
139
+
140
+ Logger.logDebug(
141
+ `Creating PBX group '${frameworkName}' under path '${groupPath}' with files: ${filePaths.join(', ')}`
142
+ );
143
+
144
+ const frameworkGroup = project.addPbxGroup(
145
+ filePaths,
146
+ frameworkName,
147
+ groupPath
148
+ );
149
+
150
+ // add the group to the main group using the proper API
151
+ const mainGroupKey = project.getFirstProject().firstProject.mainGroup;
152
+ project.addToPbxGroup(frameworkGroup.uuid, mainGroupKey);
153
+
154
+ Logger.logInfo(`Successfully added framework target: ${frameworkName}`);
155
+
156
+ return {
157
+ frameworkTargetUUID: frameworkTarget.uuid,
158
+ targetAlreadyExists: false,
159
+ };
160
+ }
161
+
162
+ export function addSourceFilesBuildPhase(
163
+ project: XcodeProject,
164
+ frameworkTargetUUID: string,
165
+ options: ResolvedBrownfieldPluginIosConfig
166
+ ) {
167
+ const filePaths = getFrameworkSourceFiles(options).map(
168
+ (file) => file.relativePath
169
+ );
170
+
171
+ const sourceFiles = filePaths.filter(
172
+ (filePath) => !filePath.endsWith('.plist')
173
+ );
174
+
175
+ project.addBuildPhase(
176
+ sourceFiles,
177
+ 'PBXSourcesBuildPhase',
178
+ options.frameworkName,
179
+ frameworkTargetUUID,
180
+ 'framework',
181
+ '""'
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Returns build settings for the framework target
187
+ * @param options The user configuration
188
+ * @returns Build settings object
189
+ */
190
+ function getFrameworkBuildSettings(
191
+ {
192
+ configuration,
193
+ }: {
194
+ /** Build configuration name ("Debug" or "Release") */
195
+ configuration: 'Debug' | 'Release';
196
+ },
197
+ {
198
+ bundleIdentifier,
199
+ deploymentTarget,
200
+ frameworkName,
201
+ frameworkVersion,
202
+ buildSettings: customBuildSettings,
203
+ }: ResolvedBrownfieldPluginIosConfig
204
+ ): Record<string, string | boolean | number> {
205
+ const isDebug = configuration === 'Debug';
206
+
207
+ return {
208
+ // settings required as per https://oss.callstack.com/react-native-brownfield/docs/getting-started/ios#required-build-settings
209
+ BUILD_LIBRARY_FOR_DISTRIBUTION: 'YES',
210
+ USER_SCRIPT_SANDBOXING: 'NO',
211
+ SKIP_INSTALL: 'NO',
212
+ ENABLE_MODULE_VERIFIER: 'NO',
213
+
214
+ // basic settings
215
+ PRODUCT_BUNDLE_IDENTIFIER: `"${bundleIdentifier}"`,
216
+ IPHONEOS_DEPLOYMENT_TARGET: deploymentTarget,
217
+
218
+ // Swift settings - use modern Swift version (5.0+) to avoid legacy Swift 3.x migration prompts
219
+ SWIFT_VERSION: '5.0',
220
+ TARGETED_DEVICE_FAMILY: `"1,2"`,
221
+ INFOPLIST_FILE: `${frameworkName}/Info.plist`,
222
+ CURRENT_PROJECT_VERSION: `"${frameworkVersion}"`,
223
+ PRODUCT_NAME: '"$(TARGET_NAME)"',
224
+ SWIFT_OPTIMIZATION_LEVEL: isDebug ? '-Onone' : '-O',
225
+
226
+ // custom build settings
227
+ ...customBuildSettings,
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Finds the "Bundle React Native code and images" build phase from the main app target
233
+ * and adds it to the framework target's build phases
234
+ * @param project The Xcode project
235
+ * @param targetUuid The UUID of the framework target
236
+ */
237
+ export function copyBundleReactNativePhase(
238
+ project: XcodeProject,
239
+ targetUuid: string
240
+ ): void {
241
+ const buildPhaseName = 'Bundle React Native code and images';
242
+
243
+ // Find the existing shell script build phase
244
+ const shellScriptPhases =
245
+ project.hash.project.objects.PBXShellScriptBuildPhase;
246
+ if (!shellScriptPhases) {
247
+ throw new SourceModificationError(
248
+ `No shell script build phases found, skipping ${buildPhaseName}`
249
+ );
250
+ }
251
+
252
+ // find the phase by name
253
+ let existingPhaseUuid: string | null = null;
254
+ for (const key of Object.keys(shellScriptPhases)) {
255
+ if (key.endsWith('_comment')) continue;
256
+ const phase = shellScriptPhases[key];
257
+ if (phase.name === `"${buildPhaseName}"` || phase.name === buildPhaseName) {
258
+ existingPhaseUuid = key;
259
+ break;
260
+ }
261
+ }
262
+
263
+ if (!existingPhaseUuid) {
264
+ throw new SourceModificationError(
265
+ `Could not find "${buildPhaseName}" build phase, skipping`
266
+ );
267
+ }
268
+
269
+ // add the phase reference to the framework target's buildPhases array
270
+ const nativeTargets = project.hash.project.objects.PBXNativeTarget;
271
+ if (nativeTargets && nativeTargets[targetUuid]) {
272
+ const target = nativeTargets[targetUuid];
273
+ if (target.buildPhases) {
274
+ // check if phase is already added
275
+ if (
276
+ !target.buildPhases.some(
277
+ (phase: { value: string }) => phase.value === existingPhaseUuid
278
+ )
279
+ ) {
280
+ target.buildPhases.push({
281
+ value: existingPhaseUuid,
282
+ comment: buildPhaseName,
283
+ });
284
+
285
+ Logger.logDebug(
286
+ `Added "${buildPhaseName}" build phase to framework target ${target.name}`
287
+ );
288
+ }
289
+ }
290
+ }
291
+ }
292
+
293
+ function resolveAppTargetName(
294
+ project: XcodeProject,
295
+ modRequest: ModProps<XcodeProject>
296
+ ): string | null {
297
+ const appTargets = IOSConfig.Target.getNativeTargets(project)
298
+ .map(([, target]) => {
299
+ if (
300
+ !IOSConfig.Target.isTargetOfType(
301
+ target,
302
+ IOSConfig.Target.TargetType.APPLICATION
303
+ )
304
+ ) {
305
+ return null;
306
+ }
307
+
308
+ const name = IOSConfig.XcodeUtils.unquote(target.name ?? '').trim();
309
+
310
+ return name ?? null;
311
+ })
312
+ .filter((name): name is string => !!name);
313
+
314
+ // 1) Unambiguous first application-type target
315
+ if (appTargets.length === 1) {
316
+ return appTargets[0];
317
+ } else {
318
+ Logger.logWarning(
319
+ 'Multiple application targets found in the Xcode project. Falling back to the CNG-derived name from mod compiler.'
320
+ );
321
+ }
322
+
323
+ // 2) CNG-derived name from mod compiler (`modRequest.projectName`) - only if it exists in the filtered application-type list of Xcode project targets
324
+ const cngDerivedProjectName = modRequest.projectName;
325
+ if (cngDerivedProjectName && appTargets.includes(cngDerivedProjectName)) {
326
+ return cngDerivedProjectName;
327
+ } else {
328
+ Logger.logWarning(
329
+ 'CNG-derived name from mod compiler is not set or is not an application target. Falling back to the unfiltered-type target name.'
330
+ );
331
+ }
332
+
333
+ // 3) PBX "first native target" fallback
334
+ try {
335
+ const [, firstAppTarget] = IOSConfig.Target.findFirstNativeTarget(project);
336
+ const name = IOSConfig.XcodeUtils.unquote(firstAppTarget.name ?? '').trim();
337
+ return name || null;
338
+ } catch {
339
+ Logger.logWarning(
340
+ 'No first native target of any type found in the Xcode project. This was the last resort fallback.'
341
+ );
342
+ }
343
+
344
+ Logger.logError(
345
+ `Could not determine the iOS app target name from the Xcode project. Please adjust your Xcode project to have exactly one application target.`
346
+ );
347
+
348
+ return null;
349
+ }
350
+
351
+ export function addExpoPre55ShellPatchScriptPhase(
352
+ modRequest: ModProps<XcodeProject>,
353
+ project: XcodeProject,
354
+ {
355
+ frameworkName,
356
+ frameworkTargetUUID,
357
+ }: {
358
+ frameworkName: string;
359
+ frameworkTargetUUID: string;
360
+ }
361
+ ) {
362
+ const resolvedAppTargetName = resolveAppTargetName(project, modRequest);
363
+
364
+ Logger.logInfo(`Resolved iOS app target name: ${resolvedAppTargetName}`);
365
+
366
+ if (!resolvedAppTargetName) {
367
+ throw new SourceModificationError(
368
+ `Could not determine the iOS app target name from the Xcode project.`
369
+ );
370
+ }
371
+
372
+ project.addBuildPhase(
373
+ [
374
+ // no associated files
375
+ ],
376
+ 'PBXShellScriptBuildPhase',
377
+ 'Patch ExpoModulesProvider',
378
+ frameworkTargetUUID,
379
+ {
380
+ shellPath: '/bin/sh',
381
+ shellScript: renderTemplate('ios', 'patchExpoPre55.sh', {
382
+ '{{APP_TARGET_NAME}}': resolvedAppTargetName,
383
+ '{{FRAMEWORK_NAME}}': frameworkName,
384
+ }),
385
+ }
386
+ );
387
+ }
388
+
389
+ /**
390
+ * Makes sure the patch expo modules provider phase is after the expo configure phase,
391
+ * otherwise the patched file would be overwritten by the expo configure phase
392
+ * @param project The Xcode project
393
+ * @param frameworkTargetUUID The UUID of the framework target
394
+ * @returns True if the build phases were modified, false otherwise
395
+ */
396
+ export function ensureExpoPre55ShellPatchScriptPhaseIsOrdered(
397
+ project: XcodeProject,
398
+ frameworkTargetUUID: string
399
+ ) {
400
+ let modified = false;
401
+ const nativeTargetSection = project.pbxNativeTargetSection();
402
+
403
+ const buildPhases: { value: string; comment?: string }[] =
404
+ nativeTargetSection[frameworkTargetUUID].buildPhases;
405
+
406
+ const expoConfigurePhaseIndex = buildPhases.findIndex(
407
+ (phase) =>
408
+ (phase as any)?.comment?.toLowerCase() ===
409
+ '[Expo] Configure project'.toLowerCase()
410
+ );
411
+
412
+ const patchExpoModulesProviderPhaseIndex = buildPhases.findIndex(
413
+ (phase) =>
414
+ (phase as any)?.comment?.toLowerCase() ===
415
+ 'Patch ExpoModulesProvider'.toLowerCase()
416
+ );
417
+
418
+ // ensure patch expo modules provider phase is after expo configure phase
419
+ if (patchExpoModulesProviderPhaseIndex < expoConfigurePhaseIndex) {
420
+ const element = buildPhases.splice(
421
+ patchExpoModulesProviderPhaseIndex,
422
+ 1
423
+ )[0]; // pop the element at patchExpoModulesProviderPhaseIndex
424
+ buildPhases.splice(expoConfigurePhaseIndex, 0, element); // insert the element at expoConfigurePhaseIndex ("after")
425
+ modified = true;
426
+ }
427
+
428
+ nativeTargetSection[frameworkTargetUUID].buildPhases = buildPhases;
429
+
430
+ project.writeSync();
431
+
432
+ return modified;
433
+ }
@@ -0,0 +1,29 @@
1
+ const LOG_TAG = '[react-native-brownfield]';
2
+
3
+ export class Logger {
4
+ private static debug: boolean = false;
5
+
6
+ static setIsDebug(enabled: boolean) {
7
+ this.debug = enabled;
8
+ }
9
+
10
+ static logInfo(message: string, ...args: any[]) {
11
+ console.log(`${LOG_TAG} ${message}`, ...args);
12
+ }
13
+
14
+ static logDebug(message: string, ...args: any[]) {
15
+ if (!this.debug) {
16
+ return;
17
+ }
18
+
19
+ console.debug(`${LOG_TAG} ${message}`, ...args);
20
+ }
21
+
22
+ static logWarning(message: string, ...args: any[]) {
23
+ console.warn(`${LOG_TAG} ${message}`, ...args);
24
+ }
25
+
26
+ static logError(message: string, ...args: any[]) {
27
+ console.error(`${LOG_TAG} ${message}`, ...args);
28
+ }
29
+ }
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
+
4
+ </manifest>
@@ -0,0 +1,54 @@
1
+ package {{PACKAGE_NAME}}
2
+
3
+ import android.app.Application
4
+ import android.content.res.Configuration
5
+ import com.callstack.reactnativebrownfield.OnJSBundleLoaded
6
+ import com.callstack.reactnativebrownfield.ReactNativeBrownfield
7
+ import com.facebook.react.PackageList
8
+ import com.facebook.react.ReactHost
9
+ import com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
10
+ import com.facebook.react.ReactPackage
11
+ import com.facebook.react.defaults.DefaultReactNativeHost
12
+ import expo.modules.ApplicationLifecycleDispatcher
13
+ import expo.modules.ExpoReactHostFactory
14
+ import expo.modules.ReactNativeHostWrapper
15
+
16
+ object ReactNativeHostManager {
17
+ fun initialize(application: Application, onJSBundleLoaded: OnJSBundleLoaded? = null) {
18
+ loadReactNative(application)
19
+
20
+ ApplicationLifecycleDispatcher.onApplicationCreate(application)
21
+
22
+ val reactNativeHost = ReactNativeHostWrapper(
23
+ application,
24
+ object : DefaultReactNativeHost(application) {
25
+ override fun getUseDeveloperSupport(): Boolean {
26
+ return BuildConfig.DEBUG
27
+ }
28
+
29
+ override fun getPackages(): List<ReactPackage> {
30
+ return PackageList(application).packages
31
+ }
32
+
33
+ override fun getJSMainModuleName(): String {
34
+ return ".expo/.virtual-metro-entry"
35
+ }
36
+
37
+ override fun getBundleAssetName(): String = "index.android.bundle"
38
+ })
39
+
40
+
41
+ val reactHost: ReactHost by lazy {
42
+ ExpoReactHostFactory.createFromReactNativeHost(
43
+ context = application.applicationContext,
44
+ reactNativeHost = reactNativeHost
45
+ )
46
+ }
47
+
48
+ ReactNativeBrownfield.initialize(application, reactHost, onJSBundleLoaded)
49
+ }
50
+
51
+ fun onConfigurationChanged(application: Application, newConfig: Configuration) {
52
+ ApplicationLifecycleDispatcher.onConfigurationChanged(application, newConfig)
53
+ }
54
+ }
@@ -0,0 +1,90 @@
1
+ import groovy.json.JsonOutput
2
+ import groovy.json.JsonSlurper
3
+
4
+ plugins {
5
+ id("com.android.library")
6
+ id("org.jetbrains.kotlin.android")
7
+ id("com.callstack.react.brownfield")
8
+ `maven-publish`
9
+ id("com.facebook.react")
10
+ }
11
+
12
+ publishing {
13
+ publications {
14
+ create<MavenPublication>("mavenAar") {
15
+ groupId = "{{GROUP_ID}}"
16
+ artifactId = "{{ARTIFACT_ID}}"
17
+ version = "{{ARTIFACT_VERSION}}"
18
+ afterEvaluate {
19
+ from(components.getByName("default"))
20
+ }
21
+ }
22
+ }
23
+
24
+ repositories {
25
+ mavenLocal() // publishes to the local Maven repository (~/.m2/repository by default)
26
+ }
27
+ }
28
+
29
+ react {
30
+ autolinkLibrariesWithApp()
31
+ }
32
+
33
+ android {
34
+ namespace = "{{PACKAGE_NAME}}"
35
+ compileSdk = {{COMPILE_SDK_VERSION}}
36
+
37
+ defaultConfig {
38
+ minSdk = {{MIN_SDK_VERSION}}
39
+
40
+ buildConfigField(
41
+ "boolean",
42
+ "IS_EDGE_TO_EDGE_ENABLED",
43
+ properties["edgeToEdgeEnabled"].toString()
44
+ )
45
+ buildConfigField(
46
+ "boolean",
47
+ "IS_NEW_ARCHITECTURE_ENABLED",
48
+ properties["newArchEnabled"].toString()
49
+ )
50
+ buildConfigField("boolean", "IS_HERMES_ENABLED", properties["hermesEnabled"].toString())
51
+
52
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
53
+ consumerProguardFiles("consumer-rules.pro")
54
+ }
55
+
56
+ buildFeatures {
57
+ buildConfig = true
58
+ }
59
+
60
+ buildTypes {
61
+ release {
62
+ isMinifyEnabled = false
63
+ proguardFiles(
64
+ getDefaultProguardFile("proguard-android-optimize.txt"),
65
+ "proguard-rules.pro"
66
+ )
67
+ }
68
+ }
69
+ compileOptions {
70
+ sourceCompatibility = JavaVersion.VERSION_17
71
+ targetCompatibility = JavaVersion.VERSION_17
72
+ }
73
+ kotlinOptions {
74
+ jvmTarget = "17"
75
+ }
76
+
77
+ publishing {
78
+ multipleVariants {
79
+ allVariants()
80
+ }
81
+ }
82
+ }
83
+
84
+ dependencies {
85
+ api("com.facebook.react:react-android:{{RN_VERSION}}")
86
+ api("com.facebook.react:hermes-android:{{RN_VERSION}}")
87
+
88
+ api("io.coil-kt.coil3:coil-compose:3.2.0")
89
+ api("io.coil-kt.coil3:coil-network-okhttp:3.2.0")
90
+ }
@@ -0,0 +1,8 @@
1
+ android.useAndroidX=true
2
+
3
+ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
4
+ newArchEnabled=true
5
+ hermesEnabled=true
6
+ edgeToEdgeEnabled=false
7
+
8
+ org.gradle.caching=true
@@ -0,0 +1,21 @@
1
+ # Add project specific ProGuard rules here.
2
+ # You can control the set of applied configuration files using the
3
+ # proguardFiles setting in build.gradle.
4
+ #
5
+ # For more details, see
6
+ # http://developer.android.com/guide/developing/tools/proguard.html
7
+
8
+ # If your project uses WebView with JS, uncomment the following
9
+ # and specify the fully qualified class name to the JavaScript interface
10
+ # class:
11
+ #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12
+ # public *;
13
+ #}
14
+
15
+ # Uncomment this to preserve the line number information for
16
+ # debugging stack traces.
17
+ #-keepattributes SourceFile,LineNumberTable
18
+
19
+ # If you keep the line number information, uncomment this to
20
+ # hide the original source file name.
21
+ #-renamesourcefileattribute SourceFile
@@ -0,0 +1,26 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+
4
+ /**
5
+ * Renders a template file for the specified platform with given parameters
6
+ * @param platform The platform name (specifies the subdirectory of template/)
7
+ * @param name The template file name (with extension)
8
+ * @param params The params to be replaced in the template
9
+ * @returns The rendered template content
10
+ */
11
+ export function renderTemplate(
12
+ platform: 'ios' | 'android',
13
+ name: string,
14
+ params?: Record<`{{${string}}}`, unknown>
15
+ ): string {
16
+ const templatePath = path.join(__dirname, platform, name);
17
+
18
+ let templateContent = fs.readFileSync(templatePath, 'utf8');
19
+
20
+ for (const [key, value] of Object.entries(params ?? {})) {
21
+ const regex = new RegExp(key, 'g');
22
+ templateContent = templateContent.replace(regex, String(value));
23
+ }
24
+
25
+ return templateContent;
26
+ }
@@ -0,0 +1,13 @@
1
+ import Foundation
2
+ import ReactBrownfield
3
+
4
+ // Initializes a Bundle instance that points at the framework target.
5
+ public let ReactNativeBundle = Bundle(for: InternalClassForBundle.self)
6
+
7
+ class InternalClassForBundle {}
8
+
9
+ extension ReactNativeBrownfield {
10
+ public func ensureExpoModulesProvider() {
11
+ let _ = ExpoModulesProvider()
12
+ }
13
+ }