Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- Fix boolean options from `sentry.options.json` being ignored on Android when using `RNSentrySDK.init` ([#6130](https://github.com/getsentry/sentry-react-native/pull/6130))
- Fix `includeWebFeedback: false` Metro config option causing crash at startup ([#6150](https://github.com/getsentry/sentry-react-native/pull/6150))
- Fix `sentry-expo-upload-sourcemaps` failing for projects with `devEngines.packageManager` set to non-npm managers ([#6155](https://github.com/getsentry/sentry-react-native/pull/6155))
- Fix Metro serializer wrapper breaking `getModulesRunBeforeMainModule` for third-party plugins like `react-native-worklets` `bundleMode` ([#6188](https://github.com/getsentry/sentry-react-native/pull/6188))

### Dependencies

Expand Down
16 changes: 9 additions & 7 deletions packages/core/src/js/tools/sentryOptionsSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,15 @@ export function withSentryOptionsFromFile(config: MetroConfig, optionsFile: stri
return originalSerializer(entryPoint, preModules, graph, options);
};

return {
...config,
serializer: {
...config.serializer,
customSerializer: sentryOptionsSerializer,
},
};
// Preserve Expo's __originalSerializer marker so Expo can detect user-provided serializers
if ('__originalSerializer' in originalSerializer) {
Object.assign(sentryOptionsSerializer, {
__originalSerializer: (originalSerializer as Record<string, unknown>).__originalSerializer,
});
}

config.serializer.customSerializer = sentryOptionsSerializer;
return config;
}

function createSentryOptionsModule(filePath: string): Module<VirtualJSOutput> | null {
Expand Down
47 changes: 47 additions & 0 deletions packages/core/test/tools/sentryOptionsSerializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,53 @@ describe('Sentry Options Serializer', () => {
expect(actualResult).toEqual(mockedResult);
});

test('mutates config in place to preserve object identity for Expo serializer closures', () => {
const config = {
projectRoot: '/test',
serializer: {
customSerializer: customSerializerMock,
getModulesRunBeforeMainModule: jest.fn(),
},
};
const originalSerializerObj = config.serializer;

const result = withSentryOptionsFromFile(config, true);

expect(result).toBe(config);
expect(result.serializer).toBe(originalSerializerObj);
expect(result.serializer?.customSerializer).not.toBe(customSerializerMock);
expect(result.serializer?.getModulesRunBeforeMainModule).toBe(config.serializer.getModulesRunBeforeMainModule);
});

test('preserves __originalSerializer marker from Expo serializer', () => {
const expoSerializer = Object.assign(jest.fn(), { __originalSerializer: null });
const config = {
projectRoot: '/test',
serializer: {
customSerializer: expoSerializer,
},
};

const result = withSentryOptionsFromFile(config, true);

expect('__originalSerializer' in (result.serializer?.customSerializer as Record<string, unknown>)).toBe(true);
expect((result.serializer?.customSerializer as Record<string, unknown>).__originalSerializer).toBeNull();
});

test('does not add __originalSerializer when original serializer lacks it', () => {
const plainSerializer = jest.fn();
const config = {
projectRoot: '/test',
serializer: {
customSerializer: plainSerializer,
},
};

const result = withSentryOptionsFromFile(config, true);

expect('__originalSerializer' in (result.serializer?.customSerializer as Record<string, unknown>)).toBe(false);
});

test('uses custom file path when optionsFile is a string', () => {
const config = () => ({
projectRoot: '/test',
Expand Down
Loading