@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.
- package/CHANGELOG.md +11 -0
- package/lib/commonjs/expo-config-plugin/android/withAndroidModuleFiles.js +1 -1
- package/lib/commonjs/expo-config-plugin/android/withAndroidModuleFiles.js.map +1 -1
- package/lib/commonjs/expo-config-plugin/ios/podfileHelpers.js +1 -1
- package/lib/commonjs/expo-config-plugin/ios/podfileHelpers.js.map +1 -1
- package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js +1 -1
- package/lib/commonjs/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -1
- package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js +1 -1
- package/lib/commonjs/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -1
- package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js +2 -2
- package/lib/commonjs/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
- package/lib/commonjs/expo-config-plugin/template/android/ReactNativeHostManager.vanilla.kt +35 -0
- package/lib/commonjs/expo-config-plugin/template/ios/FrameworkInterface.vanilla.swift +12 -0
- package/lib/commonjs/expo-config-plugin/template/ios/PodfileTargetBlock.vanilla.rb +5 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/scaffold/index.js +2 -0
- package/lib/commonjs/scaffold/index.js.map +1 -0
- package/lib/commonjs/scaffold/projectFiles.js +2 -0
- package/lib/commonjs/scaffold/projectFiles.js.map +1 -0
- package/lib/commonjs/scaffold/xcode.d.js +2 -0
- package/lib/commonjs/scaffold/xcode.d.js.map +1 -0
- package/lib/module/expo-config-plugin/android/withAndroidModuleFiles.js +1 -1
- package/lib/module/expo-config-plugin/android/withAndroidModuleFiles.js.map +1 -1
- package/lib/module/expo-config-plugin/ios/podfileHelpers.js +1 -1
- package/lib/module/expo-config-plugin/ios/podfileHelpers.js.map +1 -1
- package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js +1 -1
- package/lib/module/expo-config-plugin/ios/withBrownfieldIos.js.map +1 -1
- package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js +1 -1
- package/lib/module/expo-config-plugin/ios/withIosFrameworkFiles.js.map +1 -1
- package/lib/module/expo-config-plugin/ios/xcodeHelpers.js +2 -2
- package/lib/module/expo-config-plugin/ios/xcodeHelpers.js.map +1 -1
- package/lib/module/expo-config-plugin/template/android/ReactNativeHostManager.vanilla.kt +35 -0
- package/lib/module/expo-config-plugin/template/ios/FrameworkInterface.vanilla.swift +12 -0
- package/lib/module/expo-config-plugin/template/ios/PodfileTargetBlock.vanilla.rb +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/scaffold/index.js +2 -0
- package/lib/module/scaffold/index.js.map +1 -0
- package/lib/module/scaffold/projectFiles.js +2 -0
- package/lib/module/scaffold/projectFiles.js.map +1 -0
- package/lib/module/scaffold/xcode.d.js +2 -0
- package/lib/module/scaffold/xcode.d.js.map +1 -0
- package/lib/typescript/commonjs/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts +10 -2
- package/lib/typescript/commonjs/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/podfileHelpers.d.ts +4 -2
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/podfileHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts +11 -2
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts +6 -2
- package/lib/typescript/commonjs/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/scaffold/index.d.ts +23 -0
- package/lib/typescript/commonjs/src/scaffold/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/scaffold/projectFiles.d.ts +63 -0
- package/lib/typescript/commonjs/src/scaffold/projectFiles.d.ts.map +1 -0
- package/lib/typescript/module/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts +10 -2
- package/lib/typescript/module/src/expo-config-plugin/android/withAndroidModuleFiles.d.ts.map +1 -1
- package/lib/typescript/module/src/expo-config-plugin/ios/podfileHelpers.d.ts +4 -2
- package/lib/typescript/module/src/expo-config-plugin/ios/podfileHelpers.d.ts.map +1 -1
- package/lib/typescript/module/src/expo-config-plugin/ios/withBrownfieldIos.d.ts.map +1 -1
- package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts +11 -2
- package/lib/typescript/module/src/expo-config-plugin/ios/withIosFrameworkFiles.d.ts.map +1 -1
- package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts +6 -2
- package/lib/typescript/module/src/expo-config-plugin/ios/xcodeHelpers.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/scaffold/index.d.ts +23 -0
- package/lib/typescript/module/src/scaffold/index.d.ts.map +1 -0
- package/lib/typescript/module/src/scaffold/projectFiles.d.ts +63 -0
- package/lib/typescript/module/src/scaffold/projectFiles.d.ts.map +1 -0
- package/package.json +16 -2
- package/src/expo-config-plugin/android/withAndroidModuleFiles.ts +33 -5
- package/src/expo-config-plugin/ios/podfileHelpers.ts +24 -6
- package/src/expo-config-plugin/ios/withBrownfieldIos.ts +5 -2
- package/src/expo-config-plugin/ios/withIosFrameworkFiles.ts +21 -6
- package/src/expo-config-plugin/ios/xcodeHelpers.ts +77 -5
- package/src/expo-config-plugin/template/android/ReactNativeHostManager.vanilla.kt +35 -0
- package/src/expo-config-plugin/template/ios/FrameworkInterface.vanilla.swift +12 -0
- package/src/expo-config-plugin/template/ios/PodfileTargetBlock.vanilla.rb +5 -0
- package/src/index.ts +7 -0
- package/src/scaffold/index.ts +395 -0
- package/src/scaffold/projectFiles.ts +243 -0
- 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(
|
|
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(
|
|
161
|
-
'
|
|
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
|
|
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
|
-
|
|
77
|
-
|
|
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
|
|
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:
|
|
25
|
-
'
|
|
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 =
|
|
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
|
+
|
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 {
|