Skip to content

Commit 18c4361

Browse files
authored
Merge d25db30 into b4ee16b
2 parents b4ee16b + d25db30 commit 18c4361

File tree

7 files changed

+511
-11
lines changed

7 files changed

+511
-11
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@
1010

1111
### Features
1212

13+
- Add RNSentrySDK APIs support to @sentry/react-native/expo plugin ([#4633](https://github.com/getsentry/sentry-react-native/pull/4633))
14+
15+
This feature is opt-out, to enable it set `useNativeInit` to `true` in your `@sentry/react-native/expo` plugin configuration.
16+
17+
```js
18+
"plugins": [
19+
[
20+
"@sentry/react-native/expo",
21+
{
22+
"useNativeInit": true
23+
}
24+
],
25+
```
26+
1327
- User Feedback Widget Beta ([#4435](https://github.com/getsentry/sentry-react-native/pull/4435))
1428

1529
To collect user feedback from inside your application call `Sentry.showFeedbackWidget()`.

packages/core/plugin/src/withSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface PluginProps {
1212
project?: string;
1313
authToken?: string;
1414
url?: string;
15+
useNativeInit?: boolean;
1516
experimental_android?: SentryAndroidGradlePluginOptions;
1617
}
1718

@@ -26,7 +27,7 @@ const withSentryPlugin: ConfigPlugin<PluginProps | void> = (config, props) => {
2627
let cfg = config;
2728
if (sentryProperties !== null) {
2829
try {
29-
cfg = withSentryAndroid(cfg, sentryProperties);
30+
cfg = withSentryAndroid(cfg, { sentryProperties, useNativeInit: props?.useNativeInit });
3031
} catch (e) {
3132
warnOnce(`There was a problem with configuring your native Android project: ${e}`);
3233
}
@@ -39,7 +40,7 @@ const withSentryPlugin: ConfigPlugin<PluginProps | void> = (config, props) => {
3940
}
4041
}
4142
try {
42-
cfg = withSentryIOS(cfg, sentryProperties);
43+
cfg = withSentryIOS(cfg, { sentryProperties, useNativeInit: props?.useNativeInit });
4344
} catch (e) {
4445
warnOnce(`There was a problem with configuring your native iOS project: ${e}`);
4546
}

packages/core/plugin/src/withSentryAndroid.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1+
import type { ExpoConfig } from '@expo/config-types';
12
import type { ConfigPlugin } from 'expo/config-plugins';
2-
import { withAppBuildGradle, withDangerousMod } from 'expo/config-plugins';
3+
import { withAppBuildGradle, withDangerousMod, withMainApplication } from 'expo/config-plugins';
34
import * as path from 'path';
45

56
import { warnOnce, writeSentryPropertiesTo } from './utils';
67

7-
export const withSentryAndroid: ConfigPlugin<string> = (config, sentryProperties: string) => {
8-
const cfg = withAppBuildGradle(config, config => {
8+
export const withSentryAndroid: ConfigPlugin<{ sentryProperties: string; useNativeInit: boolean | undefined }> = (
9+
config,
10+
{ sentryProperties, useNativeInit = false },
11+
) => {
12+
const appBuildGradleCfg = withAppBuildGradle(config, config => {
913
if (config.modResults.language === 'groovy') {
1014
config.modResults.contents = modifyAppBuildGradle(config.modResults.contents);
1115
} else {
1216
throw new Error('Cannot configure Sentry in the app gradle because the build.gradle is not groovy');
1317
}
1418
return config;
1519
});
16-
return withDangerousMod(cfg, [
20+
21+
const mainApplicationCfg = useNativeInit ? modifyMainApplication(appBuildGradleCfg) : appBuildGradleCfg;
22+
23+
return withDangerousMod(mainApplicationCfg, [
1724
'android',
1825
config => {
1926
writeSentryPropertiesTo(path.resolve(config.modRequest.projectRoot, 'android'), sentryProperties);
@@ -49,3 +56,58 @@ export function modifyAppBuildGradle(buildGradle: string): string {
4956

5057
return buildGradle.replace(pattern, match => `${applyFrom}\n\n${match}`);
5158
}
59+
60+
export function modifyMainApplication(config: ExpoConfig): ExpoConfig {
61+
return withMainApplication(config, async config => {
62+
if (!config.modResults || !config.modResults.path) {
63+
warnOnce('Skipping MainApplication modification because the file does not exist.');
64+
return config;
65+
}
66+
67+
const fileName = config.modResults.path.split('/').pop();
68+
69+
if (config.modResults.contents.includes('RNSentrySDK.init')) {
70+
warnOnce(`Your '${fileName}' already contains 'RNSentrySDK.init'.`);
71+
return config;
72+
}
73+
74+
if (config.modResults.language === 'java') {
75+
if (!config.modResults.contents.includes('import io.sentry.react.RNSentrySDK;')) {
76+
// Insert import statement after package declaration
77+
config.modResults.contents = config.modResults.contents.replace(
78+
/(package .*;\n\n?)/,
79+
`$1import io.sentry.react.RNSentrySDK;\n`,
80+
);
81+
}
82+
// Add RNSentrySDK.init
83+
const originalContents = config.modResults.contents;
84+
config.modResults.contents = config.modResults.contents.replace(
85+
/(super\.onCreate\(\)[;\n]*)([ \t]*)/,
86+
`$1\n$2RNSentrySDK.init(this);\n$2`,
87+
);
88+
if (config.modResults.contents === originalContents) {
89+
warnOnce(`Failed to insert 'RNSentrySDK.init'.`);
90+
}
91+
} else {
92+
// Kotlin
93+
if (!config.modResults.contents.includes('import io.sentry.react.RNSentrySDK')) {
94+
// Insert import statement after package declaration
95+
config.modResults.contents = config.modResults.contents.replace(
96+
/(package .*\n\n?)/,
97+
`$1import io.sentry.react.RNSentrySDK\n`,
98+
);
99+
}
100+
// Add RNSentrySDK.init
101+
const originalContents = config.modResults.contents;
102+
config.modResults.contents = config.modResults.contents.replace(
103+
/(super\.onCreate\(\)[;\n]*)([ \t]*)/,
104+
`$1\n$2RNSentrySDK.init(this)\n$2`,
105+
);
106+
if (config.modResults.contents === originalContents) {
107+
warnOnce(`Failed to insert 'RNSentrySDK.init'.`);
108+
}
109+
}
110+
111+
return config;
112+
});
113+
}

packages/core/plugin/src/withSentryIOS.ts

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import type { ExpoConfig } from '@expo/config-types';
12
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
23
import type { ConfigPlugin, XcodeProject } from 'expo/config-plugins';
3-
import { withDangerousMod, withXcodeProject } from 'expo/config-plugins';
4+
import { withAppDelegate, withDangerousMod, withXcodeProject } from 'expo/config-plugins';
45
import * as path from 'path';
56

67
import { warnOnce, writeSentryPropertiesTo } from './utils';
@@ -12,8 +13,11 @@ const SENTRY_REACT_NATIVE_XCODE_PATH =
1213
const SENTRY_REACT_NATIVE_XCODE_DEBUG_FILES_PATH =
1314
"`${NODE_BINARY:-node} --print \"require('path').dirname(require.resolve('@sentry/react-native/package.json')) + '/scripts/sentry-xcode-debug-files.sh'\"`";
1415

15-
export const withSentryIOS: ConfigPlugin<string> = (config, sentryProperties: string) => {
16-
const cfg = withXcodeProject(config, config => {
16+
export const withSentryIOS: ConfigPlugin<{ sentryProperties: string; useNativeInit: boolean | undefined }> = (
17+
config,
18+
{ sentryProperties, useNativeInit = false },
19+
) => {
20+
const xcodeProjectCfg = withXcodeProject(config, config => {
1721
const xcodeProject: XcodeProject = config.modResults;
1822

1923
const sentryBuildPhase = xcodeProject.pbxItemByComment(
@@ -36,7 +40,9 @@ export const withSentryIOS: ConfigPlugin<string> = (config, sentryProperties: st
3640
return config;
3741
});
3842

39-
return withDangerousMod(cfg, [
43+
const appDelegateCfc = useNativeInit ? modifyAppDelegate(xcodeProjectCfg) : xcodeProjectCfg;
44+
45+
return withDangerousMod(appDelegateCfc, [
4046
'ios',
4147
config => {
4248
writeSentryPropertiesTo(path.resolve(config.modRequest.projectRoot, 'ios'), sentryProperties);
@@ -79,3 +85,58 @@ export function addSentryWithBundledScriptsToBundleShellScript(script: string):
7985
(match: string) => `/bin/sh ${SENTRY_REACT_NATIVE_XCODE_PATH} ${match}`,
8086
);
8187
}
88+
89+
export function modifyAppDelegate(config: ExpoConfig): ExpoConfig {
90+
return withAppDelegate(config, async config => {
91+
if (!config.modResults || !config.modResults.path) {
92+
warnOnce('Skipping AppDelegate modification because the file does not exist.');
93+
return config;
94+
}
95+
96+
const fileName = config.modResults.path.split('/').pop();
97+
98+
if (config.modResults.language === 'swift') {
99+
if (config.modResults.contents.includes('RNSentrySDK.start()')) {
100+
warnOnce(`Your '${fileName}' already contains 'RNSentrySDK.start()'.`);
101+
return config;
102+
}
103+
if (!config.modResults.contents.includes('import RNSentry')) {
104+
// Insert import statement after UIKit import
105+
config.modResults.contents = config.modResults.contents.replace(/(import UIKit\n)/, `$1import RNSentry\n`);
106+
}
107+
// Add RNSentrySDK.start() at the beginning of application method
108+
const originalContents = config.modResults.contents;
109+
config.modResults.contents = config.modResults.contents.replace(
110+
/(func application\([^)]*\) -> Bool \{)/s,
111+
`$1\n RNSentrySDK.start()`,
112+
);
113+
if (config.modResults.contents === originalContents) {
114+
warnOnce(`Failed to insert 'RNSentrySDK.start()'.`);
115+
}
116+
} else {
117+
// Objective-C
118+
if (config.modResults.contents.includes('[RNSentrySDK start]')) {
119+
warnOnce(`Your '${fileName}' already contains '[RNSentrySDK start]'.`);
120+
return config;
121+
}
122+
if (!config.modResults.contents.includes('#import <RNSentry/RNSentry.h>')) {
123+
// Add import after AppDelegate.h
124+
config.modResults.contents = config.modResults.contents.replace(
125+
/(#import "AppDelegate.h"\n)/,
126+
`$1#import <RNSentry/RNSentry.h>\n`,
127+
);
128+
}
129+
// Add [RNSentrySDK start] at the beginning of application:didFinishLaunchingWithOptions method
130+
const originalContents = config.modResults.contents;
131+
config.modResults.contents = config.modResults.contents.replace(
132+
/(- \(BOOL\)application:[\s\S]*?didFinishLaunchingWithOptions:[\s\S]*?\{\n)(\s*)/s,
133+
`$1$2[RNSentrySDK start];\n$2`,
134+
);
135+
if (config.modResults.contents === originalContents) {
136+
warnOnce(`Failed to insert '[RNSentrySDK start]'.`);
137+
}
138+
}
139+
140+
return config;
141+
});
142+
}

0 commit comments

Comments
 (0)