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 @@ -11,6 +11,7 @@
### Fixes

- Android SDK not being disabled when `options.enabled` is set to `false` ([#5334](https://github.com/getsentry/sentry-react-native/pull/5334))
- Fixes how bundle IDs are getting defined for individual bundles ([#5342](https://github.com/getsentry/sentry-react-native/pull/5342))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could add a reference to 0.83.2+ here


### Dependencies

Expand Down
16 changes: 12 additions & 4 deletions packages/core/src/js/tools/sentryMetroSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,19 @@ export const createSentryMetroSerializer = (customSerializer?: MetroSerializer):
const { code: bundleCode, map: bundleMapString } = await extractSerializerResult(serializerResult);

// Add debug id comment to the bundle
const debugId = determineDebugIdFromBundleSource(bundleCode);
let debugId = determineDebugIdFromBundleSource(bundleCode);
if (!debugId) {
throw new Error(
'Debug ID was not found in the bundle. Call `options.sentryBundleCallback` if you are using a custom serializer.',
);
// For lazy-loaded chunks or bundles without the debug ID module,
// calculate the debug ID from the bundle content.
// This ensures Metro 0.83.2+ code-split bundles get debug IDs.
// That needs to be done because when Metro 0.83.2 stopped importing `BabelSourceMapSegment`
// from `@babel/generator` and defined it locally, it subtly changed the source map output format.
// https://github.com/facebook/metro/blob/main/packages/metro-source-map/src/source-map.js#L47
const hash = crypto.createHash('md5');
hash.update(bundleCode);
debugId = stringToUUID(hash.digest('hex'));
// eslint-disable-next-line no-console
console.log('info ' + `Bundle Debug ID (calculated): ${debugId}`);
}
// Only print debug id for command line builds => not hot reload from dev server
// eslint-disable-next-line no-console
Expand Down
43 changes: 43 additions & 0 deletions packages/core/test/tools/sentryMetroSerializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,49 @@ describe('Sentry Metro Serializer', () => {
expect(debugId).toMatch(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/);
}
});

test('calculates debug id from bundle code when debug id module is not found', async () => {
// Create a custom serializer that returns bundle code without the debug ID module
const customSerializer: MetroSerializer = async () => {
const bundleCodeWithoutDebugId = 'console.log("test bundle");';
return {
code: bundleCodeWithoutDebugId,
map: '{"version":3,"sources":[],"names":[],"mappings":""}',
};
};

const serializer = createSentryMetroSerializer(customSerializer);
const bundle = await serializer(...mockMinSerializerArgs());

if (typeof bundle === 'string') {
fail('Expected bundle to be an object with a "code" property');
}

// The debug ID should be calculated from the bundle code content
// and added as a comment in the bundle code
expect(bundle.code).toContain('//# debugId=');

// Extract the debug ID from the comment
const debugIdMatch = bundle.code.match(/\/\/# debugId=([0-9a-fA-F-]+)/);
expect(debugIdMatch).toBeTruthy();
const debugId = debugIdMatch?.[1];

// Verify it's a valid UUID format
expect(debugId).toMatch(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/);

// Verify the debug ID is also in the source map
const sourceMap = JSON.parse(bundle.map);
expect(sourceMap.debug_id).toBe(debugId);
expect(sourceMap.debugId).toBe(debugId);

// The calculated debug ID should be deterministic based on the bundle content
// Running the serializer again with the same content should produce the same debug ID
const bundle2 = await serializer(...mockMinSerializerArgs());
if (typeof bundle2 !== 'string') {
const debugIdMatch2 = bundle2.code.match(/\/\/# debugId=([0-9a-fA-F-]+)/);
expect(debugIdMatch2?.[1]).toBe(debugId);
}
});
});

function mockMinSerializerArgs(options?: {
Expand Down
Loading