Skip to content

Commit 72fc84a

Browse files
authored
Fix how bundle IDs are getting defined for individual bundles (#5342)
* Fix how bundle IDs are getting defined for individual bundles * Changelog update * Added test
1 parent fbcff0b commit 72fc84a

File tree

3 files changed

+56
-4
lines changed

3 files changed

+56
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
### Fixes
1212

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

1516
### Dependencies
1617

packages/core/src/js/tools/sentryMetroSerializer.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,19 @@ export const createSentryMetroSerializer = (customSerializer?: MetroSerializer):
7474
const { code: bundleCode, map: bundleMapString } = await extractSerializerResult(serializerResult);
7575

7676
// Add debug id comment to the bundle
77-
const debugId = determineDebugIdFromBundleSource(bundleCode);
77+
let debugId = determineDebugIdFromBundleSource(bundleCode);
7878
if (!debugId) {
79-
throw new Error(
80-
'Debug ID was not found in the bundle. Call `options.sentryBundleCallback` if you are using a custom serializer.',
81-
);
79+
// For lazy-loaded chunks or bundles without the debug ID module,
80+
// calculate the debug ID from the bundle content.
81+
// This ensures Metro 0.83.2+ code-split bundles get debug IDs.
82+
// That needs to be done because when Metro 0.83.2 stopped importing `BabelSourceMapSegment`
83+
// from `@babel/generator` and defined it locally, it subtly changed the source map output format.
84+
// https://github.com/facebook/metro/blob/main/packages/metro-source-map/src/source-map.js#L47
85+
const hash = crypto.createHash('md5');
86+
hash.update(bundleCode);
87+
debugId = stringToUUID(hash.digest('hex'));
88+
// eslint-disable-next-line no-console
89+
console.log('info ' + `Bundle Debug ID (calculated): ${debugId}`);
8290
}
8391
// Only print debug id for command line builds => not hot reload from dev server
8492
// eslint-disable-next-line no-console

packages/core/test/tools/sentryMetroSerializer.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,49 @@ describe('Sentry Metro Serializer', () => {
6565
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}$/);
6666
}
6767
});
68+
69+
test('calculates debug id from bundle code when debug id module is not found', async () => {
70+
// Create a custom serializer that returns bundle code without the debug ID module
71+
const customSerializer: MetroSerializer = async () => {
72+
const bundleCodeWithoutDebugId = 'console.log("test bundle");';
73+
return {
74+
code: bundleCodeWithoutDebugId,
75+
map: '{"version":3,"sources":[],"names":[],"mappings":""}',
76+
};
77+
};
78+
79+
const serializer = createSentryMetroSerializer(customSerializer);
80+
const bundle = await serializer(...mockMinSerializerArgs());
81+
82+
if (typeof bundle === 'string') {
83+
fail('Expected bundle to be an object with a "code" property');
84+
}
85+
86+
// The debug ID should be calculated from the bundle code content
87+
// and added as a comment in the bundle code
88+
expect(bundle.code).toContain('//# debugId=');
89+
90+
// Extract the debug ID from the comment
91+
const debugIdMatch = bundle.code.match(/\/\/# debugId=([0-9a-fA-F-]+)/);
92+
expect(debugIdMatch).toBeTruthy();
93+
const debugId = debugIdMatch?.[1];
94+
95+
// Verify it's a valid UUID format
96+
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}$/);
97+
98+
// Verify the debug ID is also in the source map
99+
const sourceMap = JSON.parse(bundle.map);
100+
expect(sourceMap.debug_id).toBe(debugId);
101+
expect(sourceMap.debugId).toBe(debugId);
102+
103+
// The calculated debug ID should be deterministic based on the bundle content
104+
// Running the serializer again with the same content should produce the same debug ID
105+
const bundle2 = await serializer(...mockMinSerializerArgs());
106+
if (typeof bundle2 !== 'string') {
107+
const debugIdMatch2 = bundle2.code.match(/\/\/# debugId=([0-9a-fA-F-]+)/);
108+
expect(debugIdMatch2?.[1]).toBe(debugId);
109+
}
110+
});
68111
});
69112

70113
function mockMinSerializerArgs(options?: {

0 commit comments

Comments
 (0)