Skip to content

Commit 78feee8

Browse files
Feat(Expo): Add expo constants on event context. (#5748)
* add expo constants * changelog * Apply suggestion from @lucas-zimerman * add back "useNativeInit": true, * Apply suggestions from code review * update type
1 parent a6ce440 commit 78feee8

File tree

7 files changed

+382
-11
lines changed

7 files changed

+382
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
### Features
1212

13+
- Add expo constants on event context ([#5748](https://github.com/getsentry/sentry-react-native/pull/5748))
1314
- Capture dynamic route params as span attributes for Expo Router navigations ([#5750](https://github.com/getsentry/sentry-react-native/pull/5750))
1415

1516
### Fixes

packages/core/src/js/integrations/default.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
dedupeIntegration,
1818
deviceContextIntegration,
1919
eventOriginIntegration,
20+
expoConstantsIntegration,
2021
expoContextIntegration,
2122
functionToStringIntegration,
2223
hermesProfilingIntegration,
@@ -131,6 +132,7 @@ export function getDefaultIntegrations(options: ReactNativeClientOptions): Integ
131132
}
132133

133134
integrations.push(expoContextIntegration());
135+
integrations.push(expoConstantsIntegration());
134136

135137
if (options.spotlight && __DEV__) {
136138
const sidecarUrl = typeof options.spotlight === 'string' ? options.spotlight : undefined;
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import type { Event, Integration } from '@sentry/core';
2+
import { isExpo } from '../utils/environment';
3+
import type { ExpoConstants } from '../utils/expoglobalobject';
4+
import { getExpoConstants } from '../utils/expomodules';
5+
6+
const INTEGRATION_NAME = 'ExpoConstants';
7+
8+
export const EXPO_CONSTANTS_CONTEXT_KEY = 'expo_constants';
9+
10+
/** Load Expo Constants as event context. */
11+
export const expoConstantsIntegration = (): Integration => {
12+
let _expoConstantsContextCached: ExpoConstantsContext | undefined;
13+
14+
function processEvent(event: Event): Event {
15+
if (!isExpo()) {
16+
return event;
17+
}
18+
19+
event.contexts = event.contexts || {};
20+
event.contexts[EXPO_CONSTANTS_CONTEXT_KEY] = {
21+
...getExpoConstantsContextCached(),
22+
};
23+
24+
return event;
25+
}
26+
27+
function getExpoConstantsContextCached(): ExpoConstantsContext {
28+
if (_expoConstantsContextCached) {
29+
return _expoConstantsContextCached;
30+
}
31+
32+
return (_expoConstantsContextCached = getExpoConstantsContext());
33+
}
34+
35+
return {
36+
name: INTEGRATION_NAME,
37+
processEvent,
38+
};
39+
};
40+
41+
/**
42+
* @internal Exposed for testing purposes
43+
*/
44+
export function getExpoConstantsContext(): ExpoConstantsContext {
45+
const expoConstants = getExpoConstants();
46+
if (!expoConstants) {
47+
return {};
48+
}
49+
50+
const context: ExpoConstantsContext = {};
51+
52+
addStringField(context, 'execution_environment', expoConstants.executionEnvironment);
53+
addStringField(context, 'app_ownership', expoConstants.appOwnership);
54+
addBooleanField(context, 'debug_mode', expoConstants.debugMode);
55+
addStringField(context, 'expo_version', expoConstants.expoVersion);
56+
addStringField(context, 'expo_runtime_version', expoConstants.expoRuntimeVersion);
57+
addStringField(context, 'session_id', expoConstants.sessionId);
58+
addNumberField(context, 'status_bar_height', expoConstants.statusBarHeight);
59+
60+
addExpoConfigFields(context, expoConstants);
61+
addEasConfigFields(context, expoConstants);
62+
63+
return context;
64+
}
65+
66+
function addStringField(
67+
context: ExpoConstantsContext,
68+
key: keyof ExpoConstantsContext,
69+
value: string | null | undefined,
70+
): void {
71+
if (typeof value === 'string' && value) {
72+
(context as Record<string, unknown>)[key] = value;
73+
}
74+
}
75+
76+
function addBooleanField(
77+
context: ExpoConstantsContext,
78+
key: keyof ExpoConstantsContext,
79+
value: boolean | undefined,
80+
): void {
81+
if (typeof value === 'boolean') {
82+
(context as Record<string, unknown>)[key] = value;
83+
}
84+
}
85+
86+
function addNumberField(
87+
context: ExpoConstantsContext,
88+
key: keyof ExpoConstantsContext,
89+
value: number | undefined,
90+
): void {
91+
if (typeof value === 'number') {
92+
(context as Record<string, unknown>)[key] = value;
93+
}
94+
}
95+
96+
function addExpoConfigFields(context: ExpoConstantsContext, expoConstants: ExpoConstants): void {
97+
if (!expoConstants.expoConfig) {
98+
return;
99+
}
100+
101+
addStringField(context, 'app_name', expoConstants.expoConfig.name);
102+
addStringField(context, 'app_slug', expoConstants.expoConfig.slug);
103+
addStringField(context, 'app_version', expoConstants.expoConfig.version);
104+
addStringField(context, 'expo_sdk_version', expoConstants.expoConfig.sdkVersion);
105+
}
106+
107+
function addEasConfigFields(context: ExpoConstantsContext, expoConstants: ExpoConstants): void {
108+
if (!expoConstants.easConfig) {
109+
return;
110+
}
111+
112+
addStringField(context, 'eas_project_id', expoConstants.easConfig.projectId);
113+
}
114+
115+
type ExpoConstantsContext = Partial<{
116+
execution_environment: string;
117+
app_ownership: string;
118+
debug_mode: boolean;
119+
expo_version: string;
120+
expo_runtime_version: string;
121+
session_id: string;
122+
status_bar_height: number;
123+
app_name: string;
124+
app_slug: string;
125+
app_version: string;
126+
expo_sdk_version?: string;
127+
eas_project_id: string;
128+
}>;

packages/core/src/js/integrations/exports.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export { hermesProfilingIntegration } from '../profiling/integration';
1111
export { screenshotIntegration } from './screenshot';
1212
export { viewHierarchyIntegration } from './viewhierarchy';
1313
export { expoContextIntegration } from './expocontext';
14+
export { expoConstantsIntegration } from './expoconstants';
1415
export { spotlightIntegration } from './spotlight';
1516
export { mobileReplayIntegration } from '../replay/mobilereplay';
1617
export { feedbackIntegration } from '../feedback/integration';

packages/core/src/js/utils/expoglobalobject.ts

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,9 @@
55
*
66
* https://github.com/expo/expo/blob/b51b5139f2caa2a9495e4132437d7ca612276158/packages/expo-constants/src/Constants.ts
77
* https://github.com/expo/expo/blob/b51b5139f2caa2a9495e4132437d7ca612276158/packages/expo-manifests/src/Manifests.ts
8+
* https://github.com/expo/expo/blob/fce7f6eb2ea2611cb30e9cb20baaeee2ac0a18b6/packages/expo-constants/src/Constants.types.ts
89
*/
910
export interface ExpoConstants {
10-
/**
11-
* Deprecated. But until removed we can use it as user ID to match the native SDKs.
12-
*/
13-
installationId?: string;
14-
/**
15-
* Version of the Expo Go app
16-
*/
1711
expoVersion?: string | null;
1812
manifest?: null | {
1913
[key: string]: unknown;
@@ -23,6 +17,52 @@ export interface ExpoConstants {
2317
*/
2418
runtimeVersion?: string;
2519
};
20+
/**
21+
* Returns the current execution environment.
22+
* Values: 'bare', 'standalone', 'storeClient'
23+
*/
24+
executionEnvironment?: string;
25+
/**
26+
* Deprecated. Returns 'expo' when running in Expo Go, otherwise null.
27+
*/
28+
appOwnership?: string | null;
29+
/**
30+
* Identifies debug vs. production builds.
31+
*/
32+
debugMode?: boolean;
33+
/**
34+
* Unique identifier per app session.
35+
*/
36+
sessionId?: string;
37+
/**
38+
* Runtime version info.
39+
*/
40+
expoRuntimeVersion?: string | null;
41+
/**
42+
* Device status bar height.
43+
*/
44+
statusBarHeight?: number;
45+
/**
46+
* Available system fonts.
47+
*/
48+
systemFonts?: string[];
49+
/**
50+
* The standard Expo config object defined in app.json and app.config.js files.
51+
*/
52+
expoConfig?: null | {
53+
[key: string]: unknown;
54+
name?: string;
55+
slug?: string;
56+
version?: string;
57+
sdkVersion?: string;
58+
};
59+
/**
60+
* EAS configuration when applicable.
61+
*/
62+
easConfig?: null | {
63+
[key: string]: unknown;
64+
projectId?: string;
65+
};
2666
}
2767

2868
/**

0 commit comments

Comments
 (0)