mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
451fffbb59
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
171 lines
5.0 KiB
JavaScript
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();
|
|
};
|
|
}
|