Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,15 @@ jobs:
strategy:
fail-fast: false # keeps matrix running if one fails
matrix:
rn-version: ['0.65.3', '0.79.1']
rn-version: ['0.65.3', '0.80.0']
rn-architecture: ['legacy', 'new']
platform: ['android', 'ios']
build-type: ['production']
ios-use-frameworks: ['no', 'static', 'dynamic']
engine: ['hermes', 'jsc']
include:
- platform: ios
rn-version: '0.79.1'
rn-version: '0.80.0'
xcode-version: '16.2'
runs-on: macos-15
- platform: ios
Expand All @@ -182,7 +182,7 @@ jobs:
runs-on: ubuntu-latest
exclude:
# exclude JSC for new RN versions (keeping the matrix manageable)
- rn-version: '0.79.1'
- rn-version: '0.80.0'
engine: 'jsc'
# exclude all rn versions lower than 0.70.0 for new architecture
- rn-version: '0.65.3'
Expand Down Expand Up @@ -301,15 +301,15 @@ jobs:
strategy:
fail-fast: false # keeps matrix running if one fails
matrix:
rn-version: ['0.65.3', '0.79.1']
rn-version: ['0.65.3', '0.80.0']
rn-architecture: ['legacy', 'new']
platform: ['android', 'ios']
build-type: ['production']
ios-use-frameworks: ['no'] # test only no framworks
engine: ['hermes', 'jsc']
include:
- platform: ios
rn-version: '0.79.1'
rn-version: '0.80.0'
runs-on: macos-15
- platform: ios
rn-version: '0.65.3'
Expand All @@ -323,7 +323,7 @@ jobs:
# e2e test only the default combinations
- rn-version: '0.65.3'
engine: 'hermes'
- rn-version: '0.79.1'
- rn-version: '0.80.0'
engine: 'jsc'

steps:
Expand Down
21 changes: 21 additions & 0 deletions dev-packages/e2e-tests/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ const testApp = `${e2eDir}/${testAppName}`;
const appId = platform === 'ios' ? 'org.reactjs.native.example.RnDiffApp' : 'com.rndiffapp';
const sentryAuthToken = env.SENTRY_AUTH_TOKEN;

function runCodegenIfNeeded(rnVersion, platform, appDir) {
const versionNumber = parseFloat(rnVersion.replace(/[^\d.]/g, ''));
const shouldRunCodegen = platform === 'android' && versionNumber >= 0.80;

if (shouldRunCodegen) {
console.log(`Running codegen for React Native ${rnVersion}...`);
try {
execSync('./gradlew generateCodegenArtifactsFromSchema', {
stdio: 'inherit',
cwd: path.join(appDir, 'android'),
env: env
});
console.log('Gradle codegen task completed successfully');
} catch (error) {
console.error('Codegen failed:', error.message);
}
}
}

// Build and publish the SDK - we only need to do this once in CI.
// Locally, we may want to get updates from the latest build so do it on every app build.
if (actions.includes('create') || (env.CI === undefined && actions.includes('build'))) {
Expand Down Expand Up @@ -198,6 +217,8 @@ if (actions.includes('build')) {

appProduct = `${appDir}/ios/DerivedData/Build/Products/${buildType}-iphonesimulator/${appName}.app`;
} else if (platform == 'android') {
runCodegenIfNeeded(RNVersion, platform, appDir);

execSync(`./gradlew assemble${buildType} -PreactNativeArchitectures=x86 --no-daemon`, {
stdio: 'inherit',
cwd: `${appDir}/android`,
Expand Down
4 changes: 3 additions & 1 deletion dev-packages/e2e-tests/patch-scripts/rn.patch.app.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ Sentry.init({
const e2eComponentPatch = '<EndToEndTestsScreen />';
const lastImportRex = /^([^]*)(import\s+[^;]*?;$)/m;
const patchRex = '@sentry/react-native';
const headerComponentRex = /<ScrollView/gm;
// Support both older RN versions with ScrollView and newer versions with NewAppScreen
const headerComponentRex = /(<ScrollView|<NewAppScreen)/gm;
const exportDefaultRex = /export\s+default\s+App;/m;

const jsPath = path.join(args.app, 'App.js');
const tsxPath = path.join(args.app, 'App.tsx');
Expand Down
15 changes: 12 additions & 3 deletions packages/core/RNSentry.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ rn_package = parse_rn_package_json()
rn_version = get_rn_version(rn_package)
is_hermes_default = is_hermes_default(rn_version)
is_profiling_supported = is_profiling_supported(rn_version)
is_new_hermes_runtime = is_new_hermes_runtime(rn_version)

folly_flags = ' -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1'
folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32'
# Use different Folly configuration for RN 0.80.0+
if should_use_folly_flags(rn_version)
# For older RN versions, keep the original Folly configuration
folly_flags = ' -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1'
folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32'
else
# For RN 0.80+, don't use the incompatible Folly flags
folly_compiler_flags = ''
end

is_new_arch_enabled = ENV["RCT_NEW_ARCH_ENABLED"] == "1"
is_using_hermes = (ENV['USE_HERMES'] == nil && is_hermes_default) || ENV['USE_HERMES'] == '1'
new_arch_enabled_flag = (is_new_arch_enabled ? folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED" : "")
sentry_profiling_supported_flag = (is_profiling_supported ? " -DSENTRY_PROFILING_SUPPORTED=1" : "")
other_cflags = "$(inherited)" + new_arch_enabled_flag + sentry_profiling_supported_flag
new_hermes_runtime_flag = (is_new_hermes_runtime ? " -DNEW_HERMES_RUNTIME" : "")
other_cflags = "$(inherited)" + new_arch_enabled_flag + sentry_profiling_supported_flag + new_hermes_runtime_flag

Pod::Spec.new do |s|
s.name = 'RNSentry'
Expand Down
19 changes: 18 additions & 1 deletion packages/core/ios/RNSentry.mm
Original file line number Diff line number Diff line change
Expand Up @@ -810,7 +810,15 @@ + (SentryUser *_Nullable)userFrom:(NSDictionary *)userKeys
{
#if SENTRY_PROFILING_ENABLED
try {
# ifdef NEW_HERMES_RUNTIME
auto *hermesAPI = facebook::jsi::castInterface<facebook::hermes::IHermesRootAPI>(
facebook::hermes::makeHermesRootAPI());
if (hermesAPI) {
hermesAPI->enableSamplingProfiler();
}
# else
facebook::hermes::HermesRuntime::enableSamplingProfiler();
# endif
if (nativeProfileTraceId == nil && nativeProfileStartTime == 0 && platformProfilers) {
# if SENTRY_TARGET_PROFILING_SUPPORTED
nativeProfileTraceId = [RNSentryId newId];
Expand Down Expand Up @@ -870,10 +878,19 @@ + (SentryUser *_Nullable)userFrom:(NSDictionary *)userKeys
nativeProfileTraceId = nil;
nativeProfileStartTime = 0;

facebook::hermes::HermesRuntime::disableSamplingProfiler();
std::stringstream ss;
# ifdef NEW_HERMES_RUNTIME
auto *hermesAPI = facebook::jsi::castInterface<facebook::hermes::IHermesRootAPI>(
facebook::hermes::makeHermesRootAPI());
if (hermesAPI) {
hermesAPI->disableSamplingProfiler();
hermesAPI->dumpSampledTraceToStream(ss);
}
# else
facebook::hermes::HermesRuntime::disableSamplingProfiler();
// Before RN 0.69 Hermes used llvh::raw_ostream (profiling is supported for 0.69 and newer)
facebook::hermes::HermesRuntime::dumpSampledTraceToStream(ss);
# endif

std::string s = ss.str();
NSString *data = [NSString stringWithCString:s.c_str()
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"jest-environment-jsdom": "^29.6.2",
"jest-extended": "^4.0.2",
"madge": "^6.1.0",
"metro": "0.81.0",
"metro": "0.83.1",
"prettier": "^2.0.5",
"react": "18.3.1",
"react-native": "0.77.1",
Expand Down
9 changes: 9 additions & 0 deletions packages/core/scripts/sentry_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,12 @@ def is_hermes_default(rn_version)
def is_profiling_supported(rn_version)
return (rn_version[:major] >= 1 || (rn_version[:major] == 0 && rn_version[:minor] >= 69))
end

# Check if we need the old Folly flags (for RN < 0.80.0)
def should_use_folly_flags(rn_version)
return (rn_version[:major] == 0 && rn_version[:minor] < 80)
end

def is_new_hermes_runtime(rn_version)
return (rn_version[:major] >= 1 || (rn_version[:major] == 0 && rn_version[:minor] >= 81))
end
5 changes: 3 additions & 2 deletions packages/core/src/js/tools/sentryMetroSerializer.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import * as crypto from 'crypto';
// eslint-disable-next-line import/no-extraneous-dependencies
import type { MixedOutput, Module, ReadOnlyGraph } from 'metro';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as countLines from 'metro/src/lib/countLines';

import type { Bundle, MetroSerializer, MetroSerializerOutput, SerializedBundle, VirtualJSOutput } from './utils';
import { createDebugIdSnippet, createSet, determineDebugIdFromBundleSource, stringToUUID } from './utils';
import { createDefaultMetroSerializer } from './vendor/metro/utils';

const newline = /\r\n?|\n|\u2028|\u2029/g;
const countLines = (string: string): number => (string.match(newline) || []).length + 1;

type SourceMap = Record<string, unknown>;

const DEBUG_ID_PLACE_HOLDER = '__debug_id_place_holder__';
Expand Down
45 changes: 36 additions & 9 deletions packages/core/src/js/tools/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import * as crypto from 'crypto';
// eslint-disable-next-line import/no-extraneous-dependencies
import type { Module, ReadOnlyGraph, SerializerOptions } from 'metro';
// eslint-disable-next-line import/no-extraneous-dependencies
import type CountingSet from 'metro/src/lib/CountingSet';
import type CountingSet from 'metro/src/lib/CountingSet'; // types are in src but exports are in private

// Variant of MixedOutput
// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/DeltaBundler/types.flow.js#L21
Expand Down Expand Up @@ -84,16 +83,44 @@ export function determineDebugIdFromBundleSource(code: string): string | undefin
* https://github.com/facebook/metro/blob/fc29a1177f883144674cf85a813b58567f69d545/packages/metro/src/lib/CountingSet.js
*/
function resolveSetCreator(): () => CountingSet<string> {
const CountingSetFromPrivate = safeRequireCountingSetFromPrivate();
if (CountingSetFromPrivate) {
return () => new CountingSetFromPrivate.default();
}

const CountingSetFromSrc = safeRequireCountingSetFromSrc();
if (CountingSetFromSrc) {
return () => new CountingSetFromSrc.default();
}

return () => new Set() as unknown as CountingSet<string>;
}

/**
* CountingSet was added in Metro 0.72.0 before that NodeJS Set was used.
*
* https://github.com/facebook/metro/blob/fc29a1177f883144674cf85a813b58567f69d545/packages/metro/src/lib/CountingSet.js
*/
function safeRequireCountingSetFromSrc(): { default: new <T>() => CountingSet<T> } | undefined {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies
return require('metro/src/lib/CountingSet');
} catch (e) {
return undefined;
}
}

/**
* CountingSet was moved to private in Metro 0.83.0. (all src exports were moved to private)
*
* https://github.com/facebook/metro/commit/ae6f42372ed361611b5672705f22081c2022cf28
*/
function safeRequireCountingSetFromPrivate(): { default: new <T>() => CountingSet<T> } | undefined {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-extraneous-dependencies
const { default: MetroSet } = require('metro/src/lib/CountingSet');
return () => new MetroSet();
return require('metro/private/lib/CountingSet');
} catch (e) {
if (e instanceof Error && 'code' in e && e.code === 'MODULE_NOT_FOUND') {
return () => new Set() as unknown as CountingSet<string>;
} else {
throw e;
}
return undefined;
}
}

Expand Down
11 changes: 7 additions & 4 deletions packages/core/src/js/tools/vendor/metro/metro.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

declare module 'metro/src/DeltaBundler/Serializers/baseJSBundle' {
// All exports were moved from src to private in Metro 0.83.0.
// https://github.com/facebook/metro/commit/ae6f42372ed361611b5672705f22081c2022cf28

declare module 'metro/private/DeltaBundler/Serializers/baseJSBundle' {
// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/DeltaBundler/Serializers/baseJSBundle.js#L25
const baseJSBundle: (
entryPoint: string,
Expand All @@ -39,7 +42,7 @@ declare module 'metro/src/DeltaBundler/Serializers/baseJSBundle' {
export = baseJSBundle;
}

declare module 'metro/src/lib/bundleToString' {
declare module 'metro/private/lib/bundleToString' {
// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/lib/bundleToString.js#L22
const baseJSBundle: (bundle: { modules: [number, string][]; post: string; pre: string }) => {
code: string;
Expand All @@ -49,13 +52,13 @@ declare module 'metro/src/lib/bundleToString' {
export = baseJSBundle;
}

declare module 'metro/src/lib/countLines' {
declare module 'metro/private/lib/countLines' {
// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/lib/countLines.js#L16
const countLines: (code: string) => number;
export = countLines;
}

declare module 'metro/src/DeltaBundler/Serializers/sourceMapString' {
declare module 'metro/private/DeltaBundler/Serializers/sourceMapString' {
import type { MixedOutput, Module } from 'metro';

// https://github.com/facebook/metro/blob/9b85f83c9cc837d8cd897aa7723be7da5b296067/packages/metro/src/DeltaBundler/Serializers/sourceMapString.js#L19
Expand Down
37 changes: 31 additions & 6 deletions packages/core/src/js/tools/vendor/metro/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,37 @@

// eslint-disable-next-line import/no-extraneous-dependencies
import type { MixedOutput, Module, ReadOnlyGraph } from 'metro';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as baseJSBundle from 'metro/src/DeltaBundler/Serializers/baseJSBundle';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as sourceMapString from 'metro/src/DeltaBundler/Serializers/sourceMapString';
// eslint-disable-next-line import/no-extraneous-dependencies
import * as bundleToString from 'metro/src/lib/bundleToString';
import type * as baseJSBundleType from 'metro/private/DeltaBundler/Serializers/baseJSBundle';
import type * as sourceMapStringType from 'metro/private/DeltaBundler/Serializers/sourceMapString';
import type * as bundleToStringType from 'metro/private/lib/bundleToString';

let baseJSBundle: typeof baseJSBundleType;
try {
baseJSBundle = require('metro/private/DeltaBundler/Serializers/baseJSBundle');
} catch (e) {
baseJSBundle = require('metro/src/DeltaBundler/Serializers/baseJSBundle');
}

let sourceMapString: typeof sourceMapStringType;
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const sourceMapStringModule = require('metro/private/DeltaBundler/Serializers/sourceMapString');
sourceMapString = (sourceMapStringModule as { sourceMapString: typeof sourceMapStringType }).sourceMapString;
} catch (e) {
sourceMapString = require('metro/src/DeltaBundler/Serializers/sourceMapString');
if ('sourceMapString' in sourceMapString) {
// Changed to named export in https://github.com/facebook/metro/commit/34148e61200a508923315fbe387b26d1da27bf4b
// Metro 0.81.0 and 0.80.10 patch
sourceMapString = (sourceMapString as { sourceMapString: typeof sourceMapStringType }).sourceMapString;
}
}

let bundleToString: typeof bundleToStringType;
try {
bundleToString = require('metro/private/lib/bundleToString');
} catch (e) {
bundleToString = require('metro/src/lib/bundleToString');
}

import type { MetroSerializer } from '../../utils';

Expand Down
6 changes: 4 additions & 2 deletions packages/core/test/tools/sentryMetroSerializer.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import * as fs from 'fs';
import type { MixedOutput, Module } from 'metro';
import CountingSet from 'metro/src/lib/CountingSet';
import * as countLines from 'metro/src/lib/countLines';
// eslint-disable-next-line import/no-unresolved
import CountingSet from 'metro/private/lib/CountingSet';
// eslint-disable-next-line import/no-unresolved
import * as countLines from 'metro/private/lib/countLines';
import { minify } from 'uglify-js';

import { createSentryMetroSerializer } from '../../src/js/tools/sentryMetroSerializer';
Expand Down
Loading