@@ -9,84 +9,98 @@ const PHASE_COMMENT = 'Bundle React Native code and images';
9
9
const INSTABUG_BUILD_PHASE = '[instabug-reactnative] Upload Sourcemap' ;
10
10
11
11
export const withInstabugIOS : ConfigPlugin < PluginProps > = ( config , props ) => {
12
- let cfg = withXcodeProject ( config , ( configXcode ) => {
13
- const xcodeProject : XcodeProject = configXcode . modResults ;
12
+ let updatedConfig = withXcodeProject ( config , ( configXcode ) => {
13
+ const xcodeProject = configXcode . modResults ;
14
14
const buildPhases = xcodeProject . hash . project . objects [ BUILD_PHASE ] ;
15
15
16
16
if ( ! buildPhases ) {
17
17
console . warn ( '[Instabug] No build phases found in Xcode project.' ) ;
18
18
return configXcode ;
19
19
}
20
20
21
- const findPhaseByName = ( targetName : string ) => {
22
- return Object . entries ( buildPhases ) . find ( ( [ , phase ] : any ) => {
23
- const rawName = phase ?. name ?? '' ;
24
- const cleanName = rawName
25
- . toLowerCase ( )
26
- . replace ( '[cp-user] ' , '' )
27
- . replace ( / ^ " + | " + $ / g, '' )
28
- . trim ( ) ;
29
- const target = targetName . toLowerCase ( ) . trim ( ) ;
30
- return cleanName === target ;
31
- } ) ?. [ 1 ] ;
32
- } ;
33
-
34
- // Add Instabug build phase if not present
35
- const instabugPhase = findPhaseByName ( INSTABUG_BUILD_PHASE ) ;
36
-
37
- if ( instabugPhase == null && props . forceUploadSourceMaps ) {
38
- const packagePath = require . resolve ( `${ props . name } /package.json` ) ;
39
- const packageDir = path . dirname ( packagePath ) ;
40
- const sourcemapsPath = path . join ( packageDir , 'ios/sourcemaps.sh' ) ;
41
-
42
- if ( fs . existsSync ( sourcemapsPath ) ) {
43
- xcodeProject . addBuildPhase ( [ ] , BUILD_PHASE , INSTABUG_BUILD_PHASE , null , {
44
- shellPath : '/bin/sh' ,
45
- shellScript : '/bin/sh ' + sourcemapsPath ,
46
- } ) ;
47
- } else {
48
- console . warn ( `Could not find sourcemaps.sh at path: ${ sourcemapsPath } ` ) ;
49
- }
50
- }
21
+ // Add Instabug build phase if not already present
22
+ const hasInstabugPhase = Boolean ( findBuildPhase ( buildPhases , INSTABUG_BUILD_PHASE ) ) ;
51
23
52
- const bundleReactNativePhase = xcodeProject . pbxItemByComment ( PHASE_COMMENT , BUILD_PHASE ) ;
24
+ if ( ! hasInstabugPhase && props . forceUploadSourceMaps ) {
25
+ addInstabugBuildPhase ( xcodeProject , props . name ) ;
26
+ }
53
27
54
- if ( bundleReactNativePhase ?. shellScript ) {
55
- bundleReactNativePhase . shellScript = addSourceMapExport ( bundleReactNativePhase . shellScript ) ;
28
+ // Patch bundle React Native phase with source map export
29
+ const bundlePhase = xcodeProject . pbxItemByComment ( PHASE_COMMENT , BUILD_PHASE ) ;
30
+ if ( bundlePhase ?. shellScript ) {
31
+ bundlePhase . shellScript = injectSourceMapExport ( bundlePhase . shellScript ) ;
56
32
}
57
33
58
34
return configXcode ;
59
35
} ) ;
36
+
37
+ // Add media permissions to Info.plist if enabled
60
38
if ( props . enableMediaUploadBugReporting ) {
61
- const instabugConfig = config ? .extra ?. instabug || { } ; // Read custom configuration from extra.instabug
39
+ const instabugConfig = config . extra ?. instabug ?? { } ;
62
40
63
41
const microphonePermission =
64
42
instabugConfig . microphonePermission || 'This app needs access to your microphone.' ;
65
43
const photoLibraryPermission =
66
44
instabugConfig . photoLibraryPermission || 'This app needs access to your photos.' ;
67
45
68
- // Modify Info.plist for iOS
69
- cfg = withInfoPlist ( config , ( configXcode ) => {
70
- configXcode . ios . infoPlist = {
71
- ...configXcode . ios . infoPlist ,
72
- NSMicrophoneUsageDescription : microphonePermission ,
73
- NSPhotoLibraryUsageDescription : photoLibraryPermission ,
74
- } ;
75
- return config ;
46
+ updatedConfig = withInfoPlist ( updatedConfig , ( configXcode ) => {
47
+ const plist = configXcode . ios . infoPlist ?? { } ;
48
+
49
+ if ( ! plist . NSMicrophoneUsageDescription ) {
50
+ plist . NSMicrophoneUsageDescription = microphonePermission ;
51
+ }
52
+
53
+ if ( ! plist . NSPhotoLibraryUsageDescription ) {
54
+ plist . NSPhotoLibraryUsageDescription = photoLibraryPermission ;
55
+ }
56
+
57
+ configXcode . ios . infoPlist = plist ;
58
+ return configXcode ;
76
59
} ) ;
77
60
}
78
- return cfg ;
61
+
62
+ return updatedConfig ;
79
63
} ;
80
64
81
- function addSourceMapExport ( script : string ) : string {
82
- const exportLine = 'export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"' ;
83
- const escapedLine = exportLine . replace ( / \$ / g, '\\$' ) . replace ( / " / g, '\\"' ) ;
65
+ // Find a build phase by its clean name
66
+ function findBuildPhase ( buildPhases : any , targetName : string ) : any | undefined {
67
+ const target = targetName . toLowerCase ( ) . trim ( ) ;
68
+ return Object . values ( buildPhases ) . find ( ( phase : any ) => {
69
+ const rawName = phase ?. name ?? '' ;
70
+ const cleanName = rawName
71
+ . toLowerCase ( )
72
+ . replace ( '[cp-user] ' , '' )
73
+ . replace ( / ^ " + | " + $ / g, '' )
74
+ . trim ( ) ;
75
+ return cleanName === target ;
76
+ } ) ;
77
+ }
84
78
85
- const injectedLine = `${ escapedLine } \\n` ;
79
+ // Inject Instabug shell script phase
80
+ function addInstabugBuildPhase ( xcodeProject : XcodeProject , packageName : string ) : void {
81
+ try {
82
+ const packagePath = require . resolve ( `${ packageName } /package.json` ) ;
83
+ const sourcemapScriptPath = path . join ( path . dirname ( packagePath ) , 'ios/sourcemaps.sh' ) ;
84
+
85
+ if ( ! fs . existsSync ( sourcemapScriptPath ) ) {
86
+ console . warn ( `[Instabug] sourcemaps.sh not found at: ${ sourcemapScriptPath } ` ) ;
87
+ return ;
88
+ }
86
89
87
- if ( script . includes ( escapedLine ) ) {
88
- return script ;
90
+ xcodeProject . addBuildPhase ( [ ] , BUILD_PHASE , INSTABUG_BUILD_PHASE , null , {
91
+ shellPath : '/bin/sh' ,
92
+ shellScript : `/bin/sh ${ sourcemapScriptPath } ` ,
93
+ } ) ;
94
+ } catch ( err ) {
95
+ console . warn ( `[Instabug] Failed to resolve package path for "${ packageName } ":` , err ) ;
89
96
}
97
+ }
98
+
99
+ // Inject source map export line into the shell script
100
+ function injectSourceMapExport ( script : string ) : string {
101
+ const exportLine = 'export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"' ;
102
+ const escapedLine = exportLine . replace ( / \$ / g, '\\$' ) . replace ( / " / g, '\\"' ) ;
103
+ const injectedLine = `${ escapedLine } \\n` ;
90
104
91
- return script . replace ( / ^ " / , `"${ injectedLine } ` ) ;
105
+ return script . includes ( escapedLine ) ? script : script . replace ( / ^ " / , `"${ injectedLine } ` ) ;
92
106
}
0 commit comments