mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Add option to enable experimental debugger frontend (#39227)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/39227 Changelog: [Internal] 1. Adds an `unstable_experiments` option to `createDevMiddleware` in `react-native/dev-middleware`. 2. Adds `enableCustomDebuggerFrontend` (default `false` for now) as an experiment flag controlling whether the new debugger frontend (D48680624, D48682302) is in use. We plan to enable this by default in RN 0.73 after additional testing. If enabled, the new debugger frontend will only be used for the `/open-debugger` flow Reviewed By: huntie Differential Revision: D48602725 fbshipit-source-id: 598865b559478df1f19420daf3633ee6c233362a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
b7d2d2cd4b
commit
3ec22c1e69
+39
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
declare module 'actual-request-url' {
|
||||
declare interface ActualRequestUrl$Sock {
|
||||
+encrypted?: boolean;
|
||||
+localPort?: number;
|
||||
}
|
||||
|
||||
declare export interface Req {
|
||||
+url?: string | URL | null;
|
||||
+headers?: Object;
|
||||
+socket?: ActualRequestUrl$Sock;
|
||||
}
|
||||
|
||||
declare function actualRequestUrl(req: Req): URL | null;
|
||||
declare function getForwardVal(req: Req): string | null;
|
||||
declare function getHost(req: Req): string;
|
||||
declare function getPath(req: Req): string;
|
||||
declare function getPort(req: Req): string | null;
|
||||
declare function getProto(req: Req): string;
|
||||
|
||||
declare export {
|
||||
actualRequestUrl,
|
||||
getForwardVal,
|
||||
getHost,
|
||||
getPath,
|
||||
getPort,
|
||||
getProto,
|
||||
};
|
||||
}
|
||||
@@ -23,11 +23,14 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@isaacs/ttlcache": "^1.4.1",
|
||||
"@react-native/debugger-frontend": "^0.73.0",
|
||||
"actual-request-url": "^1.0.4",
|
||||
"chrome-launcher": "^0.15.2",
|
||||
"chromium-edge-launcher": "^1.0.0",
|
||||
"connect": "^3.6.5",
|
||||
"debug": "^2.2.0",
|
||||
"node-fetch": "^2.2.0",
|
||||
"serve-static": "^1.13.1",
|
||||
"temp-dir": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -12,9 +12,14 @@
|
||||
import type {NextHandleFunction} from 'connect';
|
||||
import type {BrowserLauncher} from './types/BrowserLauncher';
|
||||
import type {EventReporter} from './types/EventReporter';
|
||||
import type {Experiments, ExperimentsConfig} from './types/Experiments';
|
||||
import type {Logger} from './types/Logger';
|
||||
|
||||
import reactNativeDebuggerFrontendPath from '@react-native/debugger-frontend';
|
||||
import connect from 'connect';
|
||||
import path from 'path';
|
||||
// $FlowFixMe[untyped-import] TODO: type serve-static
|
||||
import serveStaticMiddleware from 'serve-static';
|
||||
import openDebuggerMiddleware from './middleware/openDebuggerMiddleware';
|
||||
import InspectorProxy from './inspector-proxy/InspectorProxy';
|
||||
import DefaultBrowserLauncher from './utils/DefaultBrowserLauncher';
|
||||
@@ -26,6 +31,7 @@ type Options = $ReadOnly<{
|
||||
logger?: Logger,
|
||||
unstable_browserLauncher?: BrowserLauncher,
|
||||
unstable_eventReporter?: EventReporter,
|
||||
unstable_experiments?: ExperimentsConfig,
|
||||
}>;
|
||||
|
||||
type DevMiddlewareAPI = $ReadOnly<{
|
||||
@@ -40,10 +46,14 @@ export default function createDevMiddleware({
|
||||
logger,
|
||||
unstable_browserLauncher = DefaultBrowserLauncher,
|
||||
unstable_eventReporter,
|
||||
unstable_experiments: experimentConfig = {},
|
||||
}: Options): DevMiddlewareAPI {
|
||||
const experiments = getExperiments(experimentConfig);
|
||||
|
||||
const inspectorProxy = new InspectorProxy(
|
||||
projectRoot,
|
||||
unstable_eventReporter,
|
||||
experiments,
|
||||
);
|
||||
|
||||
const middleware = connect()
|
||||
@@ -53,6 +63,13 @@ export default function createDevMiddleware({
|
||||
logger,
|
||||
browserLauncher: unstable_browserLauncher,
|
||||
eventReporter: unstable_eventReporter,
|
||||
experiments,
|
||||
}),
|
||||
)
|
||||
.use(
|
||||
'/debugger-frontend',
|
||||
serveStaticMiddleware(path.join(reactNativeDebuggerFrontendPath), {
|
||||
fallthrough: false,
|
||||
}),
|
||||
)
|
||||
.use((...args) => inspectorProxy.processRequest(...args));
|
||||
@@ -64,3 +81,9 @@ export default function createDevMiddleware({
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function getExperiments(config: ExperimentsConfig): Experiments {
|
||||
return {
|
||||
enableCustomDebuggerFrontend: config.enableCustomDebuggerFrontend ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import type {
|
||||
PageDescription,
|
||||
} from './types';
|
||||
import type {EventReporter} from '../types/EventReporter';
|
||||
import type {Experiments} from '../types/Experiments';
|
||||
import type {IncomingMessage, ServerResponse} from 'http';
|
||||
|
||||
import url from 'url';
|
||||
@@ -52,10 +53,17 @@ export default class InspectorProxy {
|
||||
|
||||
_eventReporter: ?EventReporter;
|
||||
|
||||
constructor(projectRoot: string, eventReporter: ?EventReporter) {
|
||||
_experiments: Experiments;
|
||||
|
||||
constructor(
|
||||
projectRoot: string,
|
||||
eventReporter: ?EventReporter,
|
||||
experiments: Experiments,
|
||||
) {
|
||||
this._projectRoot = projectRoot;
|
||||
this._devices = new Map();
|
||||
this._eventReporter = eventReporter;
|
||||
this._experiments = experiments;
|
||||
}
|
||||
|
||||
// Process HTTP request sent to server. We only respond to 2 HTTP requests:
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {NextHandleFunction} from 'connect';
|
||||
import type {IncomingMessage, ServerResponse} from 'http';
|
||||
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 url from 'url';
|
||||
@@ -26,6 +27,7 @@ type Options = $ReadOnly<{
|
||||
browserLauncher: BrowserLauncher,
|
||||
logger?: Logger,
|
||||
eventReporter?: EventReporter,
|
||||
experiments: Experiments,
|
||||
}>;
|
||||
|
||||
/**
|
||||
@@ -39,6 +41,7 @@ type Options = $ReadOnly<{
|
||||
export default function openDebuggerMiddleware({
|
||||
browserLauncher,
|
||||
eventReporter,
|
||||
experiments,
|
||||
logger,
|
||||
}: Options): NextHandleFunction {
|
||||
return async (
|
||||
@@ -50,7 +53,9 @@ export default function openDebuggerMiddleware({
|
||||
const {query} = url.parse(req.url, true);
|
||||
const {appId} = query;
|
||||
|
||||
const targets = await queryInspectorTargets(getDevServerUrl(req));
|
||||
const targets = await queryInspectorTargets(
|
||||
getDevServerUrl(req, 'local'),
|
||||
);
|
||||
let target;
|
||||
|
||||
if (typeof appId === 'string') {
|
||||
@@ -80,7 +85,11 @@ export default function openDebuggerMiddleware({
|
||||
debuggerInstances.set(
|
||||
appId,
|
||||
await browserLauncher.launchDebuggerAppWindow(
|
||||
getDevToolsFrontendUrl(target.webSocketDebuggerUrl),
|
||||
getDevToolsFrontendUrl(
|
||||
target.webSocketDebuggerUrl,
|
||||
getDevServerUrl(req, 'public'),
|
||||
experiments,
|
||||
),
|
||||
),
|
||||
);
|
||||
res.end();
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
export type Experiments = $ReadOnly<{
|
||||
enableCustomDebuggerFrontend: boolean,
|
||||
}>;
|
||||
|
||||
export type ExperimentsConfig = Partial<Experiments>;
|
||||
@@ -11,13 +11,31 @@
|
||||
|
||||
import type {IncomingMessage} from 'http';
|
||||
|
||||
import {getProto, getHost} from 'actual-request-url';
|
||||
import net from 'net';
|
||||
import {TLSSocket} from 'tls';
|
||||
|
||||
/**
|
||||
* Get the base URL to address the current development server.
|
||||
*/
|
||||
export default function getDevServerUrl(req: IncomingMessage): string {
|
||||
export default function getDevServerUrl(
|
||||
/** The current HTTP request. */
|
||||
req: IncomingMessage,
|
||||
|
||||
/**
|
||||
* 'public' for a URL accessible by the same client that sent the request, or
|
||||
* 'local' for for a URL accessible from the machine running the dev server.
|
||||
*/
|
||||
kind: 'public' | 'local',
|
||||
): string {
|
||||
if (kind === 'public') {
|
||||
const host = getHost(req);
|
||||
if (host != null) {
|
||||
const scheme = getProto(req);
|
||||
return `${scheme}://${host}`;
|
||||
}
|
||||
// If we can't determine a public URL, fall back to a local URL, which *might* still work.
|
||||
}
|
||||
const scheme =
|
||||
req.socket instanceof TLSSocket && req.socket.encrypted === true
|
||||
? 'https'
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {Experiments} from '../types/Experiments';
|
||||
|
||||
/**
|
||||
* The Chrome DevTools frontend revision to use. This should be set to the
|
||||
* latest version known to be compatible with Hermes.
|
||||
@@ -23,9 +25,16 @@ const DEVTOOLS_FRONTEND_REV = 'd9568d04d7dd79269c5a655d7ada69650c5a8336'; // Chr
|
||||
*/
|
||||
export default function getDevToolsFrontendUrl(
|
||||
webSocketDebuggerUrl: string,
|
||||
devServerUrl: string,
|
||||
experiments: Experiments,
|
||||
): string {
|
||||
const urlBase = `https://chrome-devtools-frontend.appspot.com/serve_rev/@${DEVTOOLS_FRONTEND_REV}/devtools_app.html`;
|
||||
const ws = webSocketDebuggerUrl.replace(/^ws:\/\//, '');
|
||||
|
||||
if (experiments.enableCustomDebuggerFrontend) {
|
||||
const urlBase = `${devServerUrl}/debugger-frontend/rn_inspector.html`;
|
||||
return `${urlBase}?ws=${encodeURIComponent(
|
||||
ws,
|
||||
)}&sources.hide_add_folder=true`;
|
||||
}
|
||||
const urlBase = `https://chrome-devtools-frontend.appspot.com/serve_rev/@${DEVTOOLS_FRONTEND_REV}/devtools_app.html`;
|
||||
return `${urlBase}?panel=console&ws=${encodeURIComponent(ws)}`;
|
||||
}
|
||||
|
||||
@@ -3513,6 +3513,11 @@ acorn@^8.9.0:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
|
||||
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
|
||||
|
||||
actual-request-url@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/actual-request-url/-/actual-request-url-1.0.4.tgz#54454514e3715a4c60a1e26e9a49423e376a7563"
|
||||
integrity sha512-9AOnrTOkog3eM7l4Y702+BKTYL1Tvxcl4EBbKrhDTB87npkss+I1dirUox2OyZVVz95BnavGgSZanWWas43j7A==
|
||||
|
||||
adbkit-apkreader@^3.1.2:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/adbkit-apkreader/-/adbkit-apkreader-3.2.0.tgz#8d0bb1f733969e959992095ed7f2a8d658ec97a5"
|
||||
|
||||
Reference in New Issue
Block a user