Skip to content

Commit

Permalink
Expose unstable_browserLauncher option (facebook#39162)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#39162

Expose a `unstable_browserLauncher` option to `createDevMiddleware()`. This allows integrators to provide a custom implementation for opening URLs in a web browser, initially a `launchDebuggerAppWindow` method.

Customising the browser launcher implementation can be useful in cases where the dev server is running remotely and not on the developer's local machine.

Changelog: [Internal]

Reviewed By: motiz88

Differential Revision: D48647750

fbshipit-source-id: fddd56724d0e674777198100e0fd57fbbf2896fa
  • Loading branch information
huntie authored and facebook-github-bot committed Aug 25, 2023
1 parent 3c87455 commit 60c5825
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 76 deletions.
5 changes: 5 additions & 0 deletions packages/dev-middleware/src/createDevMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
*/

import type {NextHandleFunction} from 'connect';
import type {BrowserLauncher} from './types/BrowserLauncher';
import type {EventReporter} from './types/EventReporter';
import type {Logger} from './types/Logger';

import connect from 'connect';
import openDebuggerMiddleware from './middleware/openDebuggerMiddleware';
import InspectorProxy from './inspector-proxy/InspectorProxy';
import DefaultBrowserLauncher from './utils/DefaultBrowserLauncher';

type Options = $ReadOnly<{
host: string,
port: number,
projectRoot: string,
logger?: Logger,
unstable_browserLauncher?: BrowserLauncher,
unstable_eventReporter?: EventReporter,
}>;

Expand All @@ -35,6 +38,7 @@ export default function createDevMiddleware({
port,
projectRoot,
logger,
unstable_browserLauncher = DefaultBrowserLauncher,
unstable_eventReporter,
}: Options): DevMiddlewareAPI {
const inspectorProxy = new InspectorProxy(
Expand All @@ -47,6 +51,7 @@ export default function createDevMiddleware({
'/open-debugger',
openDebuggerMiddleware({
logger,
browserLauncher: unstable_browserLauncher,
eventReporter: unstable_eventReporter,
}),
)
Expand Down
1 change: 1 addition & 0 deletions packages/dev-middleware/src/index.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@

export {default as createDevMiddleware} from './createDevMiddleware';

export type {BrowserLauncher, LaunchedBrowser} from './types/BrowserLauncher';
export type {EventReporter, ReportableEvent} from './types/EventReporter';
12 changes: 6 additions & 6 deletions packages/dev-middleware/src/middleware/openDebuggerMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@
* @oncall react_native
*/

import type {LaunchedChrome} from 'chrome-launcher';
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 {Logger} from '../types/Logger';

import url from 'url';
import getDevServerUrl from '../utils/getDevServerUrl';
import launchDebuggerAppWindow from '../utils/launchDebuggerAppWindow';
import queryInspectorTargets from '../utils/queryInspectorTargets';

const debuggerInstances = new Map<string, LaunchedChrome>();
const debuggerInstances = new Map<string, ?LaunchedBrowser>();

type Options = $ReadOnly<{
browserLauncher: BrowserLauncher,
logger?: Logger,
eventReporter?: EventReporter,
}>;
Expand All @@ -36,6 +36,7 @@ type Options = $ReadOnly<{
* @see https://chromedevtools.github.io/devtools-protocol/
*/
export default function openDebuggerMiddleware({
browserLauncher,
eventReporter,
logger,
}: Options): NextHandleFunction {
Expand Down Expand Up @@ -78,12 +79,11 @@ export default function openDebuggerMiddleware({

try {
logger?.info('Launching JS debugger...');
debuggerInstances.get(appId)?.kill();
await debuggerInstances.get(appId)?.kill();
debuggerInstances.set(
appId,
await launchDebuggerAppWindow(
await browserLauncher.launchDebuggerAppWindow(
target.devtoolsFrontendUrl,
'open-debugger',
),
);
res.end();
Expand Down
31 changes: 31 additions & 0 deletions packages/dev-middleware/src/types/BrowserLauncher.js
Original file line number Diff line number Diff line change
@@ -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
*/

/**
* Represents a launched web browser instance.
*/
export type LaunchedBrowser = {
kill: () => void | Promise<void>,
...
};

/**
* An interface for integrators to provide a custom implementation for
* opening URLs in a web browser.
*/
export interface BrowserLauncher {
/**
* Attempt to open a debugger frontend URL in a browser app window,
* optionally returning an object to control the launched browser instance.
* The browser used should be capable of running Chrome DevTools.
*/
launchDebuggerAppWindow: (url: string) => Promise<LaunchedBrowser | void>;
}
76 changes: 76 additions & 0 deletions packages/dev-middleware/src/utils/DefaultBrowserLauncher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* 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 {BrowserLauncher, LaunchedBrowser} from '../types/BrowserLauncher';

import {promises as fs} from 'fs';
import path from 'path';
import osTempDir from 'temp-dir';

const ChromeLauncher = require('chrome-launcher');
const {Launcher: EdgeLauncher} = require('chromium-edge-launcher');

/**
* Default `BrowserLauncher` implementation which opens URLs on the host
* machine.
*/
const DefaultBrowserLauncher: BrowserLauncher = {
/**
* Attempt to open the debugger frontend in a Google Chrome or Microsoft Edge
* app window.
*/
launchDebuggerAppWindow: async (url: string): Promise<LaunchedBrowser> => {
let browserType = 'chrome';
let chromePath;

try {
// Locate Chrome installation path, will throw if not found
chromePath = ChromeLauncher.getChromePath();
} catch (e) {
browserType = 'edge';
chromePath = EdgeLauncher.getFirstInstallation();

if (chromePath == null) {
throw new Error(
'Unable to find a browser on the host to open the debugger. ' +
'Supported browsers: Google Chrome, Microsoft Edge.\n' +
url,
);
}
}

const userDataDir = await createTempDir(
`react-native-debugger-frontend-${browserType}`,
);
const launchedChrome = await ChromeLauncher.launch({
chromePath,
chromeFlags: [
`--app=${url}`,
`--user-data-dir=${userDataDir}`,
'--window-size=1200,600',
],
});

return {
kill: async () => launchedChrome.kill(),
};
},
};

async function createTempDir(dirName: string): Promise<string> {
const tempDir = path.join(osTempDir, dirName);

await fs.mkdir(tempDir, {recursive: true});

return tempDir;
}

export default DefaultBrowserLauncher;
70 changes: 0 additions & 70 deletions packages/dev-middleware/src/utils/launchDebuggerAppWindow.js

This file was deleted.

0 comments on commit 60c5825

Please sign in to comment.