import Protocol from 'devtools-protocol'; import { Device as MetroDevice } from 'metro-inspector-proxy'; import { CdpMessage, DeviceRequest, InspectorHandler } from './types'; import { ExpoDebuggerInfo } from '../device'; /** Android's stock emulator and other emulators such as genymotion use a standard localhost alias. */ const EMULATOR_LOCALHOST_ADDRESSES: Readonly = ['10.0.2.2', '10.0.3.2']; /** Prefix for script URLs that are alphanumeric IDs. */ const FILE_PREFIX = 'file://'; /** * Some debug clients does not support fetching source maps by URL. * By default, the `metro-inspector-proxy` inlines the source map as base64 string. * Unfortunately, that causes a multi-second delay in VS Code (±5s). * This handler disables inlining the source maps for VS Code only. */ export class VscodeDebuggerScriptParsedHandler implements InspectorHandler { constructor(private readonly device: MetroDevice) {} onDeviceMessage(message: DeviceRequest, debuggerInfo: ExpoDebuggerInfo) { if (debuggerInfo.debuggerType !== 'vscode' || message.method !== 'Debugger.scriptParsed') { return false; } // See: https://github.com/facebook/metro/blob/f43caa371a813b257cb0b42028079645a1e85e0e/packages/metro-inspector-proxy/src/Device.js#L401-L410 if (message.params.sourceMapURL) { for (let i = 0; i < EMULATOR_LOCALHOST_ADDRESSES.length; ++i) { const address = EMULATOR_LOCALHOST_ADDRESSES[i]; if (message.params.sourceMapURL.indexOf(address) >= 0) { message.params.sourceMapURL = message.params.sourceMapURL.replace(address, 'localhost'); debuggerInfo.originalSourceURLAddress = address; } } } // See: https://github.com/facebook/metro/blob/f43caa371a813b257cb0b42028079645a1e85e0e/packages/metro-inspector-proxy/src/Device.js#L431-L453 if (message.params.url) { for (let i = 0; i < EMULATOR_LOCALHOST_ADDRESSES.length; ++i) { const address = EMULATOR_LOCALHOST_ADDRESSES[i]; if (message.params.url.indexOf(address) >= 0) { message.params.url = message.params.url.replace(address, 'localhost'); debuggerInfo.originalSourceURLAddress = address; } } // Chrome doesn't download source maps if URL param is not a valid // URL. Some frameworks pass alphanumeric script ID instead of URL which causes // Chrome to not download source maps. In this case we want to prepend script ID // with 'file://' prefix. if (message.params.url.match(/^[0-9a-z]+$/)) { message.params.url = FILE_PREFIX + message.params.url; debuggerInfo.prependedFilePrefix = true; } if (message.params.scriptId != null) { this.device._scriptIdToSourcePathMapping.set(message.params.scriptId, message.params.url); } } // Block `metro-inspector-proxy`'s default source map inlining return true; } } /** @see https://chromedevtools.github.io/devtools-protocol/v8/Debugger/#event-scriptParsed */ export type DebuggerScriptParsed = CdpMessage< 'Debugger.scriptParsed', Protocol.Debugger.ScriptParsedEvent, never >;