Files
react-native/packages/dev-middleware/src/middleware/openDebuggerMiddleware.js
T
Moti Zilberman 451fffbb59 Use host-relative WebSocket URLs when launching new debugger
Summary:
Changelog: [Internal]

Uses the capability introduced in https://github.com/facebookexperimental/rn-chrome-devtools-frontend/pull/4 to avoid repeating the dev server's host:port in the `ws` / `wss` parameter we pass to the Chrome DevTools frontend. This gives us more flexibility to handle port forwarding and redirects outside of `dev-middleware`. This is mostly useful in Meta's internal VS Code remoting setup, but this particular change should work equally well in open source.

Reviewed By: huntie

Differential Revision: D54107316

fbshipit-source-id: 68d4dbf4849ca431274bfb0dc8a4e05981bdd5b5
2024-02-26 05:33:18 -08:00

171 lines
5.0 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {InspectorProxyQueries} from '../inspector-proxy/InspectorProxy';
import type {BrowserLauncher, LaunchedBrowser} from '../types/BrowserLauncher';
import type {EventReporter} from '../types/EventReporter';
import type {Experiments} from '../types/Experiments';
import type {Logger} from '../types/Logger';
import type {NextHandleFunction} from 'connect';
import type {IncomingMessage, ServerResponse} from 'http';
import getDevToolsFrontendUrl from '../utils/getDevToolsFrontendUrl';
import url from 'url';
const debuggerInstances = new Map<string, ?LaunchedBrowser>();
type Options = $ReadOnly<{
serverBaseUrl: string,
logger?: Logger,
browserLauncher: BrowserLauncher,
eventReporter?: EventReporter,
experiments: Experiments,
inspectorProxy: InspectorProxyQueries,
}>;
/**
* Open the JavaScript debugger for a given CDP target (direct Hermes debugging).
*
* Currently supports Hermes targets, opening debugger websocket URL in Chrome
* DevTools.
*
* @see https://chromedevtools.github.io/devtools-protocol/
*/
export default function openDebuggerMiddleware({
serverBaseUrl,
logger,
browserLauncher,
eventReporter,
experiments,
inspectorProxy,
}: Options): NextHandleFunction {
return async (
req: IncomingMessage,
res: ServerResponse,
next: (err?: Error) => void,
) => {
if (
req.method === 'POST' ||
(experiments.enableOpenDebuggerRedirect && req.method === 'GET')
) {
const {query} = url.parse(req.url, true);
const {appId, device}: {appId?: string, device?: string, ...} = query;
const targets = inspectorProxy.getPageDescriptions().filter(
// Only use targets with better reloading support
app =>
app.title === 'React Native Experimental (Improved Chrome Reloads)' ||
app.reactNative.capabilities?.nativePageReloads === true,
);
let target;
const launchType: 'launch' | 'redirect' =
req.method === 'POST' ? 'launch' : 'redirect';
if (typeof appId === 'string' || typeof device === 'string') {
logger?.info(
(launchType === 'launch' ? 'Launching' : 'Redirecting to') +
' JS debugger (experimental)...',
);
if (typeof device === 'string') {
target = targets.find(
_target => _target.reactNative.logicalDeviceId === device,
);
}
if (!target && typeof appId === 'string') {
target = targets.find(_target => _target.description === appId);
}
} else {
logger?.info(
(launchType === 'launch' ? 'Launching' : 'Redirecting to') +
' JS debugger for first available target...',
);
target = targets[0];
}
if (!target) {
res.writeHead(404);
res.end('Unable to find Chrome DevTools inspector target');
logger?.warn(
'No compatible apps connected. JavaScript debugging can only be used with the Hermes engine.',
);
eventReporter?.logEvent({
type: 'launch_debugger_frontend',
launchType,
status: 'coded_error',
errorCode: 'NO_APPS_FOUND',
});
return;
}
try {
switch (launchType) {
case 'launch':
const frontendInstanceId =
device != null
? 'device:' + device
: 'app:' + (appId ?? '<null>');
await debuggerInstances.get(frontendInstanceId)?.kill();
debuggerInstances.set(
frontendInstanceId,
await browserLauncher.launchDebuggerAppWindow(
getDevToolsFrontendUrl(
experiments,
target.webSocketDebuggerUrl,
serverBaseUrl,
),
),
);
res.end();
break;
case 'redirect':
res.writeHead(302, {
Location: getDevToolsFrontendUrl(
experiments,
target.webSocketDebuggerUrl,
serverBaseUrl,
{relative: true},
),
});
res.end();
break;
default:
(launchType: empty);
}
eventReporter?.logEvent({
type: 'launch_debugger_frontend',
launchType,
status: 'success',
appId: appId ?? null,
deviceId: device ?? null,
});
return;
} catch (e) {
logger?.error(
'Error launching JS debugger: ' + e.message ?? 'Unknown error',
);
res.writeHead(500);
res.end();
eventReporter?.logEvent({
type: 'launch_debugger_frontend',
launchType,
status: 'error',
error: e,
});
return;
}
}
next();
};
}