@callstack/react-native-brownfield 5.2.0 → 5.3.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.
Files changed (82) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/lib/commonjs/expo-config-plugin/android/withAndroidModuleFiles.js +1 -1
  3. package/lib/commonjs/expo-config-plugin/android/withAndroidModuleFiles.js.map +1 -1
  4. package/lib/commonjs/expo-config-plugin/ios/podfileHelpers.js +1 -1
  5. package/lib/commonjs/expo-config-plugin/ios/podfileHelpers.js.map +1 -1
  6. package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js +1 -1
  7. package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -1
  8. package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js +1 -1
  9. package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -1
  10. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js +2 -2
  11. package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  12. package/lib/commonjs/expo-config-plugin/template/android/ReactNativeHostManager.vanilla.kt +35 -0
  13. package/lib/commonjs/expo-config-plugin/template/ios/FrameworkInterface.vanilla.swift +12 -0
  14. package/lib/commonjs/expo-config-plugin/template/ios/PodfileTargetBlock.vanilla.rb +5 -0
  15. package/lib/commonjs/index.js.map +1 -1
  16. package/lib/commonjs/scaffold/index.js +2 -0
  17. package/lib/commonjs/scaffold/index.js.map +1 -0
  18. package/lib/commonjs/scaffold/projectFiles.js +2 -0
  19. package/lib/commonjs/scaffold/projectFiles.js.map +1 -0
  20. package/lib/commonjs/scaffold/xcode.d.js +2 -0
  21. package/lib/commonjs/scaffold/xcode.d.js.map +1 -0
  22. package/lib/module/expo-config-plugin/android/withAndroidModuleFiles.js +1 -1
  23. package/lib/module/expo-config-plugin/android/withAndroidModuleFiles.js.map +1 -1
  24. package/lib/module/expo-config-plugin/ios/podfileHelpers.js +1 -1
  25. package/lib/module/expo-config-plugin/ios/podfileHelpers.js.map +1 -1
  26. package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js +1 -1
  27. package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -1
  28. package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js +1 -1
  29. package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -1
  30. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js +2 -2
  31. package/lib/module/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
  32. package/lib/module/expo-config-plugin/template/android/ReactNativeHostManager.vanilla.kt +35 -0
  33. package/lib/module/expo-config-plugin/template/ios/FrameworkInterface.vanilla.swift +12 -0
  34. package/lib/module/expo-config-plugin/template/ios/PodfileTargetBlock.vanilla.rb +5 -0
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/scaffold/index.js +2 -0
  37. package/lib/module/scaffold/index.js.map +1 -0
  38. package/lib/module/scaffold/projectFiles.js +2 -0
  39. package/lib/module/scaffold/projectFiles.js.map +1 -0
  40. package/lib/module/scaffold/xcode.d.js +2 -0
  41. package/lib/module/scaffold/xcode.d.js.map +1 -0
  42. package/lib/typescript/commonjs/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts +10 -2
  43. package/lib/typescript/commonjs/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts.map +1 -1
  44. package/lib/typescript/commonjs/src/expo-config-plugin/ios/podfileHelpers.d.ts +4 -2
  45. package/lib/typescript/commonjs/src/expo-config-plugin/ios/podfileHelpers.d.ts.map +1 -1
  46. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts +11 -2
  48. package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts +6 -2
  50. package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  52. package/lib/typescript/commonjs/src/scaffold/index.d.ts +23 -0
  53. package/lib/typescript/commonjs/src/scaffold/index.d.ts.map +1 -0
  54. package/lib/typescript/commonjs/src/scaffold/projectFiles.d.ts +63 -0
  55. package/lib/typescript/commonjs/src/scaffold/projectFiles.d.ts.map +1 -0
  56. package/lib/typescript/module/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts +10 -2
  57. package/lib/typescript/module/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts.map +1 -1
  58. package/lib/typescript/module/src/expo-config-plugin/ios/podfileHelpers.d.ts +4 -2
  59. package/lib/typescript/module/src/expo-config-plugin/ios/podfileHelpers.d.ts.map +1 -1
  60. package/lib/typescript/module/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -1
  61. package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts +11 -2
  62. package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -1
  63. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts +6 -2
  64. package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
  65. package/lib/typescript/module/src/index.d.ts.map +1 -1
  66. package/lib/typescript/module/src/scaffold/index.d.ts +23 -0
  67. package/lib/typescript/module/src/scaffold/index.d.ts.map +1 -0
  68. package/lib/typescript/module/src/scaffold/projectFiles.d.ts +63 -0
  69. package/lib/typescript/module/src/scaffold/projectFiles.d.ts.map +1 -0
  70. package/package.json +16 -2
  71. package/src/expo-config-plugin/android/withAndroidModuleFiles.ts +33 -5
  72. package/src/expo-config-plugin/ios/podfileHelpers.ts +24 -6
  73. package/src/expo-config-plugin/ios/withBrownfieldIos.ts +5 -2
  74. package/src/expo-config-plugin/ios/withIosFrameworkFiles.ts +21 -6
  75. package/src/expo-config-plugin/ios/xcodeHelpers.ts +77 -5
  76. package/src/expo-config-plugin/template/android/ReactNativeHostManager.vanilla.kt +35 -0
  77. package/src/expo-config-plugin/template/ios/FrameworkInterface.vanilla.swift +12 -0
  78. package/src/expo-config-plugin/template/ios/PodfileTargetBlock.vanilla.rb +5 -0
  79. package/src/index.ts +7 -0
  80. package/src/scaffold/index.ts +395 -0
  81. package/src/scaffold/projectFiles.ts +243 -0
  82. package/src/scaffold/xcode.d.ts +4 -0
@@ -62,8 +62,15 @@ export function resolveTargetSdkVersionExpression(
62
62
 
63
63
  export function renderTargetSdkBlock(
64
64
  config: ResolvedBrownfieldPluginConfigWithAndroid,
65
- expoMajor?: number
65
+ expoMajor?: number,
66
+ templateVariant: 'expo' | 'vanilla' = 'expo'
66
67
  ): string {
68
+ // RN CLI (vanilla) projects target AGP 9, where library modules must not
69
+ // set `targetSdk` (compilation fails with "Unresolved reference 'targetSdk'").
70
+ if (templateVariant === 'vanilla') {
71
+ return '';
72
+ }
73
+
67
74
  if (
68
75
  expoMajor !== undefined &&
69
76
  expoMajor >= EXPO_SDK_OMIT_TARGET_SDK_FROM_MAJOR
@@ -83,11 +90,22 @@ export function createAndroidModule({
83
90
  rnVersion,
84
91
  projectRoot,
85
92
  expoMajor,
93
+ templateVariant = 'expo',
86
94
  }: {
87
95
  /**
88
96
  * Expo app root (used to detect optional dependencies such as expo-updates)
89
97
  */
90
98
  projectRoot?: string;
99
+
100
+ /**
101
+ * Source template flavor. 'vanilla' is used by the RN CLI scaffold for
102
+ * non-Expo projects; Expo keeps the default 'expo' variant.
103
+ * Note: expresses the same Expo-vs-vanilla axis as `useExpoHost` in the
104
+ * iOS helpers (withIosFrameworkFiles/xcodeHelpers), with opposite
105
+ * polarity: 'vanilla' here === `useExpoHost: false` there.
106
+ */
107
+ templateVariant?: 'expo' | 'vanilla';
108
+
91
109
  /**
92
110
  * The root Android directory path
93
111
  */
@@ -116,7 +134,11 @@ export function createAndroidModule({
116
134
  const hermesArtifact = getHermesArtifact(rnVersion, projectRoot);
117
135
  const compileSdkVersionExpression =
118
136
  resolveCompileSdkVersionExpression(config);
119
- const targetSdkBlock = renderTargetSdkBlock(config, expoMajor);
137
+ const targetSdkBlock = renderTargetSdkBlock(
138
+ config,
139
+ expoMajor,
140
+ templateVariant
141
+ );
120
142
  const minifyEnabled =
121
143
  (android as { minifyEnabled?: boolean }).minifyEnabled ?? false;
122
144
  const extraProguardRules =
@@ -157,9 +179,15 @@ export function createAndroidModule({
157
179
  },
158
180
  {
159
181
  relativePath: `src/main/java/${config.android.packageName.replace(/\./g, '/')}/ReactNativeHostManager.kt`,
160
- content: renderTemplate('android', 'ReactNativeHostManager.post55.kt', {
161
- '{{PACKAGE_NAME}}': android.packageName,
162
- }),
182
+ content: renderTemplate(
183
+ 'android',
184
+ templateVariant === 'vanilla'
185
+ ? 'ReactNativeHostManager.vanilla.kt'
186
+ : 'ReactNativeHostManager.post55.kt',
187
+ {
188
+ '{{PACKAGE_NAME}}': android.packageName,
189
+ }
190
+ ),
163
191
  },
164
192
  {
165
193
  relativePath: 'consumer-rules.pro',
@@ -54,13 +54,15 @@ function ensureExpoDefinesForSDK55AndAbove(
54
54
  * Modifies the Podfile to include the Brownfield framework target
55
55
  * @param podfile The original Podfile content
56
56
  * @param frameworkName The name of the framework target to add
57
- * @param expoMajor The major version of the Expo SDK
57
+ * @param expoMajor The major version of the Expo SDK. Omit for vanilla
58
+ * (non-Expo) projects: the vanilla target block is inserted and no
59
+ * Expo-only hooks are applied.
58
60
  * @returns The modified Podfile content
59
61
  */
60
62
  export function modifyPodfile(
61
63
  podfile: string,
62
64
  frameworkName: string,
63
- expoMajor: number
65
+ expoMajor?: number
64
66
  ): string {
65
67
  // check if the framework target is already included
66
68
  if (podfile.includes(`target '${frameworkName}'`)) {
@@ -73,9 +75,18 @@ export function modifyPodfile(
73
75
  Logger.logDebug(`Modifying Podfile for framework: ${frameworkName}`);
74
76
 
75
77
  // insert the framework target after the main target's "do"
76
- const frameworkTargetBlock = renderTemplate('ios', 'PodfileTargetBlock.rb', {
77
- '{{FRAMEWORK_NAME}}': frameworkName,
78
- });
78
+ // Vanilla means the caller passed no Expo SDK version at all (undefined).
79
+ // Negative values mean "Expo detected but unknown version" and must still
80
+ // hit the unsupported-version error path below, not the vanilla path.
81
+ const frameworkTargetBlock = renderTemplate(
82
+ 'ios',
83
+ typeof expoMajor === 'number'
84
+ ? 'PodfileTargetBlock.rb'
85
+ : 'PodfileTargetBlock.vanilla.rb',
86
+ {
87
+ '{{FRAMEWORK_NAME}}': frameworkName,
88
+ }
89
+ );
79
90
 
80
91
  // find insertion point after the first target's content begins, before the end of the target block
81
92
  const mainTargetMatch = podfile.match(
@@ -101,8 +112,15 @@ export function modifyPodfile(
101
112
 
102
113
  Logger.logDebug(`Added framework target "${frameworkName}" to Podfile`);
103
114
 
115
+ // Vanilla projects (expoMajor === undefined) get no Expo-only hooks.
116
+ // Early-returning here lets TypeScript narrow `expoMajor` to `number`
117
+ // for the rest of the function (no casts needed below).
118
+ if (typeof expoMajor !== 'number') {
119
+ return modifiedPodfile;
120
+ }
121
+
104
122
  if (expoMajor < MIN_SUPPORTED_EXPO_SDK_MAJOR_VERSION) {
105
- const versionLabel = expoMajor < 0 ? 'unknown' : expoMajor.toString();
123
+ const versionLabel = expoMajor < 0 ? 'unknown' : String(expoMajor);
106
124
  throw new SourceModificationError(
107
125
  `Expo SDK ${versionLabel} is not supported. Please use Expo SDK ${MIN_SUPPORTED_EXPO_SDK_MAJOR_VERSION} or newer. For older versions, please see the matrix of supported versions: https://oss.callstack.com/react-native-brownfield/docs/getting-started/introduction#expo-version-compatibility`
108
126
  );
@@ -47,7 +47,8 @@ export const withBrownfieldIos: ConfigPlugin<
47
47
  const { frameworkTargetUUID, targetAlreadyExists } = addFrameworkTarget(
48
48
  project,
49
49
  modRequest,
50
- iosProps
50
+ iosProps,
51
+ { useExpoHost: true }
51
52
  );
52
53
 
53
54
  // Ensure Expo.plist is present in the framework resources phase when
@@ -73,7 +74,9 @@ export const withBrownfieldIos: ConfigPlugin<
73
74
  // copy the "Bundle React Native code and images" build phase from the main target to the framework target
74
75
  copyBundleReactNativePhase(project, frameworkTargetUUID);
75
76
 
76
- addSourceFilesBuildPhase(project, frameworkTargetUUID, iosProps);
77
+ addSourceFilesBuildPhase(project, frameworkTargetUUID, iosProps, {
78
+ useExpoHost: true,
79
+ });
77
80
 
78
81
  return xcodeConfig;
79
82
  });
@@ -16,14 +16,28 @@ import { renderTemplate } from '../template/engine';
16
16
  * @returns The list of framework source files
17
17
  */
18
18
  export function getFrameworkSourceFiles(
19
- ios: ResolvedBrownfieldPluginConfigWithIos['ios']
19
+ ios: ResolvedBrownfieldPluginConfigWithIos['ios'],
20
+ options?: {
21
+ /**
22
+ * Whether the packaged framework is expected to use the Expo host.
23
+ * This influences template selection for the generated framework sources.
24
+ * Note: expresses the same Expo-vs-vanilla axis as `templateVariant` in
25
+ * the Android helper (withAndroidModuleFiles.createAndroidModule), with
26
+ * opposite polarity: `false` here === 'vanilla' there.
27
+ */
28
+ useExpoHost?: boolean;
29
+ }
20
30
  ): RenderedTemplateFile[] {
31
+ const useExpoHost = options?.useExpoHost ?? true;
32
+
21
33
  return [
22
34
  {
23
35
  relativePath: `${ios.frameworkName}.swift`,
24
- content: renderTemplate('ios', 'FrameworkInterface.swift', {
25
- '{{BUNDLE_IDENTIFIER}}': ios.bundleIdentifier,
26
- }),
36
+ content: useExpoHost
37
+ ? renderTemplate('ios', 'FrameworkInterface.swift', {
38
+ '{{BUNDLE_IDENTIFIER}}': ios.bundleIdentifier,
39
+ })
40
+ : renderTemplate('ios', 'FrameworkInterface.vanilla.swift', {}),
27
41
  },
28
42
  {
29
43
  relativePath: 'Info.plist',
@@ -41,7 +55,8 @@ export function getFrameworkSourceFiles(
41
55
  */
42
56
  export function createIosFramework(
43
57
  iosDir: string,
44
- config: ResolvedBrownfieldPluginConfigWithIos
58
+ config: ResolvedBrownfieldPluginConfigWithIos,
59
+ options?: Parameters<typeof getFrameworkSourceFiles>[1]
45
60
  ) {
46
61
  const { ios } = config;
47
62
  const frameworkDir = path.join(iosDir, ios.frameworkName);
@@ -61,7 +76,7 @@ export function createIosFramework(
61
76
  }
62
77
 
63
78
  // write files
64
- for (const file of getFrameworkSourceFiles(ios)) {
79
+ for (const file of getFrameworkSourceFiles(ios, options)) {
65
80
  const filePath = path.join(frameworkDir, file.relativePath);
66
81
 
67
82
  fs.writeFileSync(filePath, file.content, 'utf8');
@@ -11,6 +11,43 @@ import type { ResolvedBrownfieldPluginIosConfig } from '../types';
11
11
  import { SourceModificationError } from '../errors/SourceModificationError';
12
12
  import { getFrameworkSourceFiles } from './withIosFrameworkFiles';
13
13
 
14
+ /**
15
+ * xcode@3.x addTarget() stores target names/comments quoted (e.g. name =
16
+ * "BrownfieldLib"), and the pbxproj parser keeps the quote characters on
17
+ * re-parse. That makes pbxTargetByName() (comment-based, unquoted) miss
18
+ * targets we previously wrote, so a second run would create a duplicate
19
+ * target. Match by unquoting the stored name as a fallback.
20
+ */
21
+ function findFrameworkTargetByQuotedName(
22
+ project: XcodeProject,
23
+ frameworkName: string
24
+ ): ReturnType<XcodeProject['pbxTargetByName']> {
25
+ const nativeTargets = getRawProjectObjects(project).PBXNativeTarget as
26
+ | Record<string, PbxNativeTarget & { isa?: string }>
27
+ | undefined;
28
+
29
+ for (const [key, target] of Object.entries(nativeTargets ?? {})) {
30
+ if (key.endsWith('_comment') || target?.isa !== 'PBXNativeTarget') {
31
+ continue;
32
+ }
33
+ const name = unquotePbxString(target.name);
34
+ if (name === frameworkName) {
35
+ return target as any;
36
+ }
37
+ }
38
+
39
+ return null;
40
+ }
41
+
42
+ function unquotePbxString(value: string | undefined): string | undefined {
43
+ if (typeof value !== 'string') {
44
+ return undefined;
45
+ }
46
+ return value.startsWith('"') && value.endsWith('"')
47
+ ? value.slice(1, -1)
48
+ : value;
49
+ }
50
+
14
51
  /**
15
52
  * Adds a new Framework target to the Xcode project for Brownfield packaging
16
53
  * @throws If target creation fails
@@ -20,7 +57,10 @@ import { getFrameworkSourceFiles } from './withIosFrameworkFiles';
20
57
  export function addFrameworkTarget(
21
58
  project: XcodeProject,
22
59
  modRequest: ModProps<XcodeProject>,
23
- options: ResolvedBrownfieldPluginIosConfig
60
+ options: ResolvedBrownfieldPluginIosConfig,
61
+ brownfieldOptions?: {
62
+ useExpoHost?: boolean;
63
+ }
24
64
  ): {
25
65
  frameworkTargetUUID: string;
26
66
  targetAlreadyExists: boolean;
@@ -28,7 +68,9 @@ export function addFrameworkTarget(
28
68
  const { frameworkName, bundleIdentifier } = options;
29
69
 
30
70
  // check if target already exists
31
- const existingTarget = project.pbxTargetByName(frameworkName);
71
+ const existingTarget =
72
+ project.pbxTargetByName(frameworkName) ??
73
+ findFrameworkTargetByQuotedName(project, frameworkName);
32
74
  if (existingTarget) {
33
75
  Logger.logDebug(
34
76
  `Framework target "${frameworkName}" already exists, skipping creation`
@@ -131,7 +173,7 @@ export function addFrameworkTarget(
131
173
  });
132
174
 
133
175
  // create the framework group in the project
134
- const filePaths = getFrameworkSourceFiles(options).map(
176
+ const filePaths = getFrameworkSourceFiles(options, brownfieldOptions).map(
135
177
  (file) => file.relativePath
136
178
  );
137
179
  const groupPath = path.join(modRequest.platformProjectRoot, frameworkName);
@@ -161,9 +203,12 @@ export function addFrameworkTarget(
161
203
  export function addSourceFilesBuildPhase(
162
204
  project: XcodeProject,
163
205
  frameworkTargetUUID: string,
164
- options: ResolvedBrownfieldPluginIosConfig
206
+ options: ResolvedBrownfieldPluginIosConfig,
207
+ brownfieldOptions?: {
208
+ useExpoHost?: boolean;
209
+ }
165
210
  ) {
166
- const filePaths = getFrameworkSourceFiles(options).map(
211
+ const filePaths = getFrameworkSourceFiles(options, brownfieldOptions).map(
167
212
  (file) => file.relativePath
168
213
  );
169
214
 
@@ -171,6 +216,26 @@ export function addSourceFilesBuildPhase(
171
216
  (filePath) => !filePath.endsWith('.plist')
172
217
  );
173
218
 
219
+ // Idempotency: skip when the target already has a sources phase with this
220
+ // comment, so re-running scaffolding does not duplicate the build phase.
221
+ const nativeTargets = getRawProjectObjects(project).PBXNativeTarget as
222
+ | Record<string, PbxNativeTarget>
223
+ | undefined;
224
+ const targetBuildPhases =
225
+ nativeTargets?.[frameworkTargetUUID]?.buildPhases ?? [];
226
+ const alreadyHasSourcePhase = targetBuildPhases.some((phase) =>
227
+ hasBuildPhaseComment(
228
+ { comment: typeof phase === 'string' ? undefined : phase.comment },
229
+ options.frameworkName
230
+ )
231
+ );
232
+ if (alreadyHasSourcePhase) {
233
+ Logger.logDebug(
234
+ `Sources build phase "${options.frameworkName}" already present on target ${frameworkTargetUUID}, skipping`
235
+ );
236
+ return;
237
+ }
238
+
174
239
  project.addBuildPhase(
175
240
  sourceFiles,
176
241
  'PBXSourcesBuildPhase',
@@ -613,6 +678,13 @@ export function copyBundleReactNativePhase(
613
678
  }
614
679
  );
615
680
 
681
+ // xcode's pbxShellScriptBuildPhaseObj only escapes quotes, leaving real
682
+ // newlines in the shellScript value — that produces an invalid pbxproj
683
+ // once the project is written. Encode both fields the same way the
684
+ // update path above does.
685
+ addedPhase.buildPhase.shellPath = encodePbxString(frameworkShellPath);
686
+ addedPhase.buildPhase.shellScript = encodePbxString(frameworkShellScript);
687
+
616
688
  if (existingPhase.showEnvVarsInLog !== undefined) {
617
689
  addedPhase.buildPhase.showEnvVarsInLog = existingPhase.showEnvVarsInLog;
618
690
  }
@@ -0,0 +1,35 @@
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.defaults.DefaultReactHost.getDefaultReactHost
11
+
12
+ object ReactNativeHostManager {
13
+ fun initialize(application: Application, onJSBundleLoaded: OnJSBundleLoaded? = null) {
14
+ loadReactNative(application)
15
+
16
+ val reactHost: ReactHost by lazy {
17
+ getDefaultReactHost(
18
+ context = application,
19
+ packageList = PackageList(application).packages,
20
+ jsMainModulePath = "index",
21
+ jsBundleAssetPath = "index.android.bundle",
22
+ jsBundleFilePath = null,
23
+ useDevSupport = BuildConfig.DEBUG,
24
+ jsRuntimeFactory = null
25
+ )
26
+ }
27
+
28
+ ReactNativeBrownfield.initialize(application, reactHost, onJSBundleLoaded)
29
+ }
30
+
31
+ fun onConfigurationChanged(application: Application, newConfig: Configuration) {
32
+ // no-op (kept for API symmetry with Expo variant)
33
+ }
34
+ }
35
+
@@ -0,0 +1,12 @@
1
+ import Foundation
2
+
3
+ // Re-export the Brownfield runtime so the native host app resolves
4
+ // ReactNativeBrownfield.shared by importing only this framework, matching the
5
+ // manual guide's interface file (docs getting-started/ios.mdx).
6
+ @_exported import ReactBrownfield
7
+
8
+ // Initializes a Bundle instance that points at the framework target.
9
+ public let ReactNativeBundle = Bundle(for: InternalClassForBundle.self)
10
+
11
+ class InternalClassForBundle {}
12
+
@@ -0,0 +1,5 @@
1
+ # Brownfield framework target for packaging as XCFramework
2
+ target '{{FRAMEWORK_NAME}}' do
3
+ inherit! :complete
4
+ end
5
+
package/src/index.ts CHANGED
@@ -2,6 +2,13 @@ import { Platform } from 'react-native';
2
2
 
3
3
  import ReactNativeBrownfieldModule from './NativeReactNativeBrownfieldModule';
4
4
 
5
+ // NOTE: the scaffold API is intentionally NOT re-exported here. This module is
6
+ // the package's `react-native`/`source` entry point, so anything reachable from
7
+ // it is traversed by Metro when an app imports the package. The scaffold pulls
8
+ // in node:fs, node:path, xcode and the Community CLI, which cannot bundle.
9
+ // Node-side consumers import it from the dedicated subpath instead:
10
+ // `@callstack/react-native-brownfield/scaffold`.
11
+
5
12
  export type { BrownfieldConfig } from '@callstack/brownfield-cli/types';
6
13
 
7
14
  export interface MessageEvent {