mirror of
https://github.com/facebook/react-native.git
synced 2024-11-21 20:50:09 +00:00
Create dev-middleware package (#38194)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/38194 ## Context RFC: Decoupling Flipper from React Native core: https://github.com/react-native-community/discussions-and-proposals/pull/641 ## Changes Inits the `react-native/dev-middleware` package. Contains an initial implementation of `/open-debugger`, migrated from2535dbe234
. ## Attribution This implementation is greatly inspired by `expo/dev-server`:1120c716f3/packages/%40expo/dev-server/src/JsInspector.ts (L18)
Changelog: [Internal] Reviewed By: motiz88 Differential Revision: D46283818 fbshipit-source-id: 7b38ad2f6d7346366a7c599d16e289e04b7bd88d
This commit is contained in:
parent
8431509407
commit
a991ff3837
49
flow-typed/npm/chrome-launcher_v0.15.x.js
vendored
Normal file
49
flow-typed/npm/chrome-launcher_v0.15.x.js
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
declare module 'chrome-launcher' {
|
||||
import typeof fs from 'fs';
|
||||
import typeof childProcess from 'child_process';
|
||||
import type {ChildProcess} from 'child_process';
|
||||
|
||||
declare export type Options = {
|
||||
startingUrl?: string,
|
||||
chromeFlags?: Array<string>,
|
||||
prefs?: mixed,
|
||||
port?: number,
|
||||
handleSIGINT?: boolean,
|
||||
chromePath?: string,
|
||||
userDataDir?: string | boolean,
|
||||
logLevel?: 'verbose' | 'info' | 'error' | 'warn' | 'silent',
|
||||
ignoreDefaultFlags?: boolean,
|
||||
connectionPollInterval?: number,
|
||||
maxConnectionRetries?: number,
|
||||
envVars?: {[key: string]: ?string},
|
||||
};
|
||||
|
||||
declare export type LaunchedChrome = {
|
||||
pid: number,
|
||||
port: number,
|
||||
process: ChildProcess,
|
||||
kill: () => void,
|
||||
};
|
||||
|
||||
declare export type ModuleOverrides = {
|
||||
fs?: fs,
|
||||
spawn?: childProcess['spawn'],
|
||||
};
|
||||
|
||||
declare class Launcher {
|
||||
launch(options: Options): Promise<LaunchedChrome>;
|
||||
}
|
||||
|
||||
declare module.exports: Launcher;
|
||||
}
|
50
flow-typed/npm/connect_v3.x.x.js
vendored
Normal file
50
flow-typed/npm/connect_v3.x.x.js
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
declare module 'connect' {
|
||||
import type http from 'http';
|
||||
|
||||
declare export type ServerHandle = HandleFunction | http.Server;
|
||||
|
||||
declare type NextFunction = (err?: mixed) => void;
|
||||
|
||||
declare export type NextHandleFunction = (
|
||||
req: IncomingMessage,
|
||||
res: http.ServerResponse,
|
||||
next: NextFunction,
|
||||
) => void | Promise<void>;
|
||||
declare export type HandleFunction = NextHandleFunction;
|
||||
|
||||
declare export interface IncomingMessage extends http.IncomingMessage {
|
||||
originalUrl?: http.IncomingMessage['url'];
|
||||
}
|
||||
|
||||
declare export interface Server extends events$EventEmitter {
|
||||
(req: IncomingMessage, res: http.ServerResponse): void;
|
||||
|
||||
use(fn: HandleFunction): Server;
|
||||
use(route: string, fn: HandleFunction): Server;
|
||||
|
||||
listen(
|
||||
port: number,
|
||||
hostname?: string,
|
||||
backlog?: number,
|
||||
callback?: Function,
|
||||
): http.Server;
|
||||
listen(port: number, hostname?: string, callback?: Function): http.Server;
|
||||
listen(path: string, callback?: Function): http.Server;
|
||||
listen(handle: any, listeningListener?: Function): http.Server;
|
||||
}
|
||||
|
||||
declare type createServer = () => Server;
|
||||
|
||||
declare module.exports: createServer;
|
||||
}
|
189
flow-typed/npm/node-fetch_v2.x.x.js
vendored
Normal file
189
flow-typed/npm/node-fetch_v2.x.x.js
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
// Modified from flow-typed repo:
|
||||
// https://github.com/flow-typed/flow-typed/blob/master/definitions/npm/node-fetch_v2.x.x/flow_v0.104.x-/node-fetch_v2.x.x.js
|
||||
|
||||
declare module 'node-fetch' {
|
||||
import type http from 'http';
|
||||
import type https from 'https';
|
||||
import type {Readable} from 'stream';
|
||||
|
||||
declare type AbortSignal = {
|
||||
+aborted: boolean,
|
||||
+onabort: (event?: {...}) => void,
|
||||
|
||||
+addEventListener: (name: string, cb: () => mixed) => void,
|
||||
+removeEventListener: (name: string, cb: () => mixed) => void,
|
||||
+dispatchEvent: (event: {...}) => void,
|
||||
...
|
||||
};
|
||||
|
||||
declare class Request mixins Body {
|
||||
constructor(
|
||||
input: string | {href: string, ...} | Request,
|
||||
init?: RequestInit,
|
||||
): this;
|
||||
context: RequestContext;
|
||||
headers: Headers;
|
||||
method: string;
|
||||
redirect: RequestRedirect;
|
||||
referrer: string;
|
||||
url: string;
|
||||
|
||||
// node-fetch extensions
|
||||
agent: http.Agent | https.Agent;
|
||||
compress: boolean;
|
||||
counter: number;
|
||||
follow: number;
|
||||
hostname: string;
|
||||
port: number;
|
||||
protocol: string;
|
||||
size: number;
|
||||
timeout: number;
|
||||
}
|
||||
|
||||
declare type HeaderObject = {[index: string]: string | number, ...};
|
||||
|
||||
declare type RequestInit = {|
|
||||
body?: BodyInit,
|
||||
headers?: HeaderObject | null,
|
||||
method?: string,
|
||||
redirect?: RequestRedirect,
|
||||
signal?: AbortSignal | null,
|
||||
|
||||
// node-fetch extensions
|
||||
agent?: (URL => http.Agent | https.Agent) | http.Agent | https.Agent | null,
|
||||
compress?: boolean,
|
||||
follow?: number,
|
||||
size?: number,
|
||||
timeout?: number,
|
||||
|};
|
||||
|
||||
declare interface FetchError extends Error {
|
||||
// cannot set name due to incompatible extend error
|
||||
// name: 'FetchError';
|
||||
type: string;
|
||||
code: ?number;
|
||||
errno: ?number;
|
||||
}
|
||||
|
||||
declare interface AbortError extends Error {
|
||||
// cannot set name due to incompatible extend error
|
||||
// name: 'AbortError';
|
||||
type: 'aborted';
|
||||
}
|
||||
|
||||
declare type RequestContext =
|
||||
| 'audio'
|
||||
| 'beacon'
|
||||
| 'cspreport'
|
||||
| 'download'
|
||||
| 'embed'
|
||||
| 'eventsource'
|
||||
| 'favicon'
|
||||
| 'fetch'
|
||||
| 'font'
|
||||
| 'form'
|
||||
| 'frame'
|
||||
| 'hyperlink'
|
||||
| 'iframe'
|
||||
| 'image'
|
||||
| 'imageset'
|
||||
| 'import'
|
||||
| 'internal'
|
||||
| 'location'
|
||||
| 'manifest'
|
||||
| 'object'
|
||||
| 'ping'
|
||||
| 'plugin'
|
||||
| 'prefetch'
|
||||
| 'script'
|
||||
| 'serviceworker'
|
||||
| 'sharedworker'
|
||||
| 'subresource'
|
||||
| 'style'
|
||||
| 'track'
|
||||
| 'video'
|
||||
| 'worker'
|
||||
| 'xmlhttprequest'
|
||||
| 'xslt';
|
||||
declare type RequestRedirect = 'error' | 'follow' | 'manual';
|
||||
|
||||
declare class Headers {
|
||||
append(name: string, value: string): void;
|
||||
delete(name: string): void;
|
||||
forEach(callback: (value: string, name: string) => void): void;
|
||||
get(name: string): string;
|
||||
getAll(name: string): Array<string>;
|
||||
has(name: string): boolean;
|
||||
raw(): {[k: string]: string[], ...};
|
||||
set(name: string, value: string): void;
|
||||
entries(): Iterator<[string, string]>;
|
||||
keys(): Iterator<string>;
|
||||
values(): Iterator<string>;
|
||||
@@iterator(): Iterator<[string, string]>;
|
||||
}
|
||||
|
||||
declare class Body {
|
||||
buffer(): Promise<Buffer>;
|
||||
json(): Promise<any>;
|
||||
json<T>(): Promise<T>;
|
||||
text(): Promise<string>;
|
||||
body: stream$Readable;
|
||||
bodyUsed: boolean;
|
||||
}
|
||||
|
||||
declare class Response mixins Body {
|
||||
constructor(body?: BodyInit, init?: ResponseInit): this;
|
||||
clone(): Response;
|
||||
error(): Response;
|
||||
redirect(url: string, status: number): Response;
|
||||
headers: Headers;
|
||||
ok: boolean;
|
||||
status: number;
|
||||
statusText: string;
|
||||
size: number;
|
||||
timeout: number;
|
||||
type: ResponseType;
|
||||
url: string;
|
||||
}
|
||||
|
||||
declare type ResponseType =
|
||||
| 'basic'
|
||||
| 'cors'
|
||||
| 'default'
|
||||
| 'error'
|
||||
| 'opaque'
|
||||
| 'opaqueredirect';
|
||||
|
||||
declare interface ResponseInit {
|
||||
headers?: HeaderInit;
|
||||
status: number;
|
||||
statusText?: string;
|
||||
}
|
||||
|
||||
declare type HeaderInit = Headers | Array<string>;
|
||||
declare type BodyInit =
|
||||
| string
|
||||
| null
|
||||
| Buffer
|
||||
| Blob
|
||||
| Readable
|
||||
| URLSearchParams;
|
||||
|
||||
declare function fetch(
|
||||
url: string | URL | Request,
|
||||
init?: RequestInit,
|
||||
): Promise<Response>;
|
||||
|
||||
declare module.exports: typeof fetch;
|
||||
}
|
14
flow-typed/npm/temp-dir_2.x.x.js
vendored
Normal file
14
flow-typed/npm/temp-dir_2.x.x.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
declare module 'temp-dir' {
|
||||
declare module.exports: string;
|
||||
}
|
13
packages/dev-middleware/.babelrc
Normal file
13
packages/dev-middleware/.babelrc
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-flow",
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "16"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
5
packages/dev-middleware/.gitignore
vendored
Normal file
5
packages/dev-middleware/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Build output
|
||||
/dist
|
11
packages/dev-middleware/README.md
Normal file
11
packages/dev-middleware/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# @react-native/dev-middleware
|
||||
|
||||
![https://img.shields.io/npm/v/@react-native/dev-middleware?color=brightgreen&label=npm%20package](https://www.npmjs.com/package/@react-native/dev-middleware)
|
||||
|
||||
Dev server middleware supporting core React Native development features. This package is preconfigured in all React Native projects.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### `/open-debugger`
|
||||
|
||||
Open the JavaScript debugger for a given CDP target (direct Hermes debugging).
|
12
packages/dev-middleware/index.js.flow
Normal file
12
packages/dev-middleware/index.js.flow
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
export * from './src';
|
43
packages/dev-middleware/package.json
Normal file
43
packages/dev-middleware/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@react-native/dev-middleware",
|
||||
"version": "0.73.0",
|
||||
"description": "Dev server middleware for React Native",
|
||||
"keywords": [
|
||||
"react-native",
|
||||
"tools"
|
||||
],
|
||||
"homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/dev-middleware#readme",
|
||||
"bugs": "https://github.com/facebook/react-native/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react-native.git",
|
||||
"directory": "packages/dev-middleware"
|
||||
},
|
||||
"license": "MIT",
|
||||
"exports": "./dist/index.js",
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "yarn clean && babel src --out-dir dist",
|
||||
"dev": "babel src --out-dir dist --source-maps --watch",
|
||||
"clean": "rimraf dist",
|
||||
"prepare": "yarn build"
|
||||
},
|
||||
"dependencies": {
|
||||
"chrome-launcher": "^0.15.2",
|
||||
"connect": "^3.6.5",
|
||||
"node-fetch": "^2.2.0",
|
||||
"temp-dir": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.20.0",
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/preset-flow": "^7.20.0",
|
||||
"rimraf": "^3.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
31
packages/dev-middleware/src/createDevMiddleware.js
Normal file
31
packages/dev-middleware/src/createDevMiddleware.js
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {NextHandleFunction} from 'connect';
|
||||
import type {Logger} from './types/Logger';
|
||||
|
||||
import connect from 'connect';
|
||||
import openDebuggerMiddleware from './middleware/openDebuggerMiddleware';
|
||||
|
||||
type Options = $ReadOnly<{
|
||||
logger?: Logger,
|
||||
}>;
|
||||
|
||||
export default function createDevMiddleware({logger}: Options = {}): {
|
||||
middleware: NextHandleFunction,
|
||||
} {
|
||||
const middleware = connect().use(
|
||||
'/open-debugger',
|
||||
openDebuggerMiddleware({logger}),
|
||||
);
|
||||
|
||||
return {middleware};
|
||||
}
|
12
packages/dev-middleware/src/index.js
Normal file
12
packages/dev-middleware/src/index.js
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
export {default as createDevMiddleware} from './createDevMiddleware';
|
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {LaunchedChrome} from 'chrome-launcher';
|
||||
import type {NextHandleFunction} from 'connect';
|
||||
import type {IncomingMessage, ServerResponse} from 'http';
|
||||
import type {Logger} from '../types/Logger';
|
||||
|
||||
import url from 'url';
|
||||
import getDevServerUrl from '../utils/getDevServerUrl';
|
||||
import launchChromeDevTools from '../utils/launchChromeDevTools';
|
||||
import queryInspectorTargets from '../utils/queryInspectorTargets';
|
||||
|
||||
const debuggerInstances = new Map<string, LaunchedChrome>();
|
||||
|
||||
type Options = $ReadOnly<{
|
||||
logger?: Logger,
|
||||
}>;
|
||||
|
||||
/**
|
||||
* 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({
|
||||
logger,
|
||||
}: Options): NextHandleFunction {
|
||||
return async (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
next: (err?: Error) => void,
|
||||
) => {
|
||||
if (req.method === 'POST') {
|
||||
const {query} = url.parse(req.url, true);
|
||||
const {appId} = query;
|
||||
|
||||
if (typeof appId !== 'string') {
|
||||
res.writeHead(400);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
const targets = await queryInspectorTargets(getDevServerUrl(req));
|
||||
const target = targets.find(_target => _target.description === appId);
|
||||
|
||||
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.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
logger?.info('Launching JS debugger...');
|
||||
debuggerInstances.get(appId)?.kill();
|
||||
debuggerInstances.set(
|
||||
appId,
|
||||
await launchChromeDevTools(target.webSocketDebuggerUrl),
|
||||
);
|
||||
res.end();
|
||||
return;
|
||||
} catch (e) {
|
||||
logger?.error(
|
||||
'Error launching JS debugger: ' + e.message ?? 'Unknown error',
|
||||
);
|
||||
res.writeHead(500);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
17
packages/dev-middleware/src/types/Logger.js
Normal file
17
packages/dev-middleware/src/types/Logger.js
Normal file
@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
export type Logger = $ReadOnly<{
|
||||
error: (...message: Array<string>) => void,
|
||||
info: (...message: Array<string>) => void,
|
||||
warn: (...message: Array<string>) => void,
|
||||
...
|
||||
}>;
|
32
packages/dev-middleware/src/utils/getDevServerUrl.js
Normal file
32
packages/dev-middleware/src/utils/getDevServerUrl.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {IncomingMessage} from 'http';
|
||||
|
||||
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 {
|
||||
const scheme =
|
||||
req.socket instanceof TLSSocket && req.socket.encrypted === true
|
||||
? 'https'
|
||||
: 'http';
|
||||
const {localAddress, localPort} = req.socket;
|
||||
const address =
|
||||
localAddress && net.isIPv6(localAddress)
|
||||
? `[${localAddress}]`
|
||||
: localAddress;
|
||||
|
||||
return `${scheme}:${address}:${localPort}`;
|
||||
}
|
38
packages/dev-middleware/src/utils/launchChromeDevTools.js
Normal file
38
packages/dev-middleware/src/utils/launchChromeDevTools.js
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {LaunchedChrome} from 'chrome-launcher';
|
||||
|
||||
import launchDebuggerAppWindow from './launchDebuggerAppWindow';
|
||||
|
||||
/**
|
||||
* The Chrome DevTools frontend revision to use. This should be set to the
|
||||
* latest version known to be compatible with Hermes.
|
||||
*
|
||||
* Revision should be the full identifier from:
|
||||
* https://chromium.googlesource.com/chromium/src.git
|
||||
*/
|
||||
const DEVTOOLS_FRONTEND_REV = 'd9568d04d7dd79269c5a655d7ada69650c5a8336'; // Chrome 100.0.4896.75
|
||||
|
||||
/**
|
||||
* Attempt to launch Chrome DevTools on the host machine for a given CDP target.
|
||||
*/
|
||||
export default async function launchChromeDevTools(
|
||||
webSocketDebuggerUrl: string,
|
||||
): Promise<LaunchedChrome> {
|
||||
const urlBase = `https://chrome-devtools-frontend.appspot.com/serve_rev/@${DEVTOOLS_FRONTEND_REV}/devtools_app.html`;
|
||||
const ws = webSocketDebuggerUrl.replace(/^ws:\/\//, '');
|
||||
|
||||
return launchDebuggerAppWindow(
|
||||
`${urlBase}?panel=console&ws=${encodeURIComponent(ws)}`,
|
||||
'open-debugger',
|
||||
);
|
||||
}
|
56
packages/dev-middleware/src/utils/launchDebuggerAppWindow.js
Normal file
56
packages/dev-middleware/src/utils/launchDebuggerAppWindow.js
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type {LaunchedChrome} from 'chrome-launcher';
|
||||
import {promises as fs} from 'fs';
|
||||
import path from 'path';
|
||||
import osTempDir from 'temp-dir';
|
||||
|
||||
const ChromeLauncher = require('chrome-launcher');
|
||||
|
||||
/**
|
||||
* Attempt to open a debugger frontend URL as a Google Chrome app window.
|
||||
*/
|
||||
export default async function launchDebuggerAppWindow(
|
||||
url: string,
|
||||
/**
|
||||
* Used to construct the temp browser dir to preserve settings such as window
|
||||
* position.
|
||||
*/
|
||||
intent: 'open-debugger',
|
||||
): Promise<LaunchedChrome> {
|
||||
const browserType = 'chrome';
|
||||
const userDataDir = await createTempDir(
|
||||
`react-native-${intent}-${browserType}`,
|
||||
);
|
||||
|
||||
try {
|
||||
return ChromeLauncher.launch({
|
||||
chromeFlags: [
|
||||
`--app=${url}`,
|
||||
`--user-data-dir=${userDataDir}`,
|
||||
'--window-size=1200,600',
|
||||
],
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
'Unable to find a browser on the host to open the debugger. Supported browsers: Google Chrome',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function createTempDir(dirName: string): Promise<string> {
|
||||
const tempDir = path.join(osTempDir, dirName);
|
||||
|
||||
await fs.mkdir(tempDir, {recursive: true});
|
||||
|
||||
return tempDir;
|
||||
}
|
40
packages/dev-middleware/src/utils/queryInspectorTargets.js
Normal file
40
packages/dev-middleware/src/utils/queryInspectorTargets.js
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
type ReactNativeCDPTarget = {
|
||||
id: string,
|
||||
description: string,
|
||||
title: string,
|
||||
type: string,
|
||||
devtoolsFrontendUrl: string,
|
||||
webSocketDebuggerUrl: string,
|
||||
vm: string,
|
||||
deviceName?: string,
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the list of available debug targets from the React Native dev server.
|
||||
*
|
||||
* @see https://chromedevtools.github.io/devtools-protocol/
|
||||
*/
|
||||
export default async function queryInspectorTargets(
|
||||
devServerUrl: string,
|
||||
): Promise<ReactNativeCDPTarget[]> {
|
||||
const res = await fetch(`${devServerUrl}/json/list`);
|
||||
const apps = (await res.json(): Array<ReactNativeCDPTarget>);
|
||||
|
||||
// Only use targets with better reloading support
|
||||
return apps.filter(
|
||||
app => app.title === 'React Native Experimental (Improved Chrome Reloads)',
|
||||
);
|
||||
}
|
42
yarn.lock
42
yarn.lock
@ -3694,6 +3694,16 @@ chownr@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
|
||||
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
|
||||
|
||||
chrome-launcher@^0.15.2:
|
||||
version "0.15.2"
|
||||
resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.15.2.tgz#4e6404e32200095fdce7f6a1e1004f9bd36fa5da"
|
||||
integrity sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
escape-string-regexp "^4.0.0"
|
||||
is-wsl "^2.2.0"
|
||||
lighthouse-logger "^1.0.0"
|
||||
|
||||
ci-info@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
|
||||
@ -4032,7 +4042,7 @@ dayjs@^1.8.15:
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.15.tgz#7121bc04e6a7f2621ed6db566be4a8aaf8c3913e"
|
||||
integrity sha512-HYHCI1nohG52B45vCQg8Re3hNDZbMroWPkhz50yaX7Lu0ATyjGsTdoYZBpjED9ar6chqTx2dmSmM8A51mojnAg==
|
||||
|
||||
debug@2.6.9, debug@^2.2.0:
|
||||
debug@2.6.9, debug@^2.2.0, debug@^2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
@ -5625,6 +5635,11 @@ is-directory@^0.3.1:
|
||||
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
|
||||
integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
|
||||
|
||||
is-docker@^2.0.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
|
||||
integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
|
||||
|
||||
is-extendable@^0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
|
||||
@ -5759,6 +5774,13 @@ is-wsl@^1.1.0:
|
||||
resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
|
||||
integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
|
||||
|
||||
is-wsl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
|
||||
integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
|
||||
dependencies:
|
||||
is-docker "^2.0.0"
|
||||
|
||||
isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
@ -6552,6 +6574,14 @@ lie@~3.3.0:
|
||||
dependencies:
|
||||
immediate "~3.0.5"
|
||||
|
||||
lighthouse-logger@^1.0.0:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz#aef90f9e97cd81db367c7634292ee22079280aaa"
|
||||
integrity sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==
|
||||
dependencies:
|
||||
debug "^2.6.9"
|
||||
marky "^1.2.2"
|
||||
|
||||
lines-and-columns@^1.1.6:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
|
||||
@ -6709,6 +6739,11 @@ makeerror@1.0.12:
|
||||
dependencies:
|
||||
tmpl "1.0.5"
|
||||
|
||||
marky@^1.2.2:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0"
|
||||
integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==
|
||||
|
||||
memfs-or-file-map-to-github-branch@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.2.1.tgz#fdb9a85408262316a9bd5567409bf89be7d72f96"
|
||||
@ -8729,6 +8764,11 @@ tar@^6.1.11:
|
||||
mkdirp "^1.0.3"
|
||||
yallist "^4.0.0"
|
||||
|
||||
temp-dir@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
|
||||
integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==
|
||||
|
||||
temp@^0.8.4:
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.4.tgz#8c97a33a4770072e0a05f919396c7665a7dd59f2"
|
||||
|
Loading…
Reference in New Issue
Block a user