1import { ExpoConfig } from '@expo/config'; 2import { Middleware } from 'metro-config'; 3 4import { env } from '../env'; 5import { DebugTool, getMetroDebugProperties } from './getMetroDebugProperties'; 6import { logEventAsync } from './rudderstackClient'; 7 8/** 9 * Create a Metro middleware that reports when a debugger request was found. 10 * This will only be reported once, if the app uses Hermes and telemetry is not enabled. 11 */ 12export function createDebuggerTelemetryMiddleware( 13 projectRoot: string, 14 exp: ExpoConfig 15): Middleware { 16 let hasReported = false; 17 18 // This only works for Hermes apps, disable when telemetry is turned off 19 if (env.EXPO_NO_TELEMETRY || exp.jsEngine !== 'hermes') { 20 return (req, res, next) => next(undefined); 21 } 22 23 return (req, res, next) => { 24 // Only report once 25 if (hasReported) { 26 return next(undefined); 27 } 28 29 const debugTool = findDebugTool(req); 30 if (debugTool) { 31 hasReported = true; 32 logEventAsync('metro debug', getMetroDebugProperties(projectRoot, exp, debugTool)); 33 } 34 35 return next(undefined); 36 }; 37} 38 39/** Exposed for testing */ 40export function findDebugTool( 41 req: Pick<Parameters<Middleware>[0], 'headers' | 'url'> 42): DebugTool | null { 43 if (req.headers['origin']?.includes('chrome-devtools')) { 44 return { name: 'chrome' }; 45 } 46 47 if (req.url?.startsWith('/json')) { 48 const flipperUserAgent = req.headers['user-agent']?.match(/(Flipper)\/([^\s]+)/); 49 if (flipperUserAgent) { 50 return { 51 name: flipperUserAgent[1].toLowerCase(), 52 version: flipperUserAgent[2], 53 }; 54 } 55 } 56 57 return null; 58} 59