Files
react-native/packages/dev-middleware/src/createDevMiddleware.js
T
Cedric van Putten 3f41fb5d5b feature(dev-middleware): add custom message handlers to extend CDP capabilities (#43291)
Summary:
This is a proposal for the `react-native/dev-middleware` package, to allow implementers to extend the CDP capabilities of the `InspectorProxy`. It's unfortunately needed until we can move to the native Hermes CDP layer.

At Expo, we extend the CDP capabilities of this `InspectorProxy` by injecting functionality on the device level. This proposed API does the same, but without having to overwrite internal functions of both the `InspectorProxy` and `InspectorDevice`.

A good example of this is the network inspector's capabilities. This currently works through the inspection proxy, and roughly like:
- Handle any incoming `Expo(Network.receivedResponseBody)` from the _**device**_, store it, and stop event from propagating
- Handle the incoming `Network.getResponseBody` from the _**debugger**_, return the data, and stop event from propagating.

This API brings back that capability in a more structured way.

## API:

```ts
import { createDevMiddleware } from 'react-native/dev-middleware';

const { middleware, websocketEndpoints } = createDevMiddleware({
  unstable_customInspectorMessageHandler: ({ page, deviceInfo, debuggerInfo }) => {
    // Do not enable handler for page other than "SOMETHING", or for vscode debugging
    // Can also include `page.capabilities` to determine if handler is required
    if (page.title !== 'SOMETHING' || debuggerInfo.userAgent?.includes('vscode')) {
      return null;
    }

    return {
      handleDeviceMessage(message) {
        if (message.type === 'CDP_MESSAGE') {
          // Do something and stop message from propagating with return `true`
          return true;
        }
      },
      handleDebuggerMessage(message) {
        if (message.type === 'CDP_MESSAGE') {
          // Do something and stop message from propagating with return `true`
          return true;
        }
      },
    };
  },
});
```

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[GENERAL] [ADDED] - Add inspector proxy device message middleware API

Pull Request resolved: https://github.com/facebook/react-native/pull/43291

Test Plan: See added tests and code above

Reviewed By: huntie

Differential Revision: D54804503

Pulled By: motiz88

fbshipit-source-id: ae918dcd5b7e76d3fb31db4c84717567ae60fa96
2024-03-12 09:58:51 -07:00

134 lines
4.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 {CreateCustomMessageHandlerFn} from './inspector-proxy/CustomMessageHandler';
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 type {NextHandleFunction} from 'connect';
import InspectorProxy from './inspector-proxy/InspectorProxy';
import deprecated_openFlipperMiddleware from './middleware/deprecated_openFlipperMiddleware';
import openDebuggerMiddleware from './middleware/openDebuggerMiddleware';
import DefaultBrowserLauncher from './utils/DefaultBrowserLauncher';
import reactNativeDebuggerFrontendPath from '@react-native/debugger-frontend';
import connect from 'connect';
import path from 'path';
import serveStaticMiddleware from 'serve-static';
type Options = $ReadOnly<{
projectRoot: string,
/**
* The base URL to the dev server, as addressible from the local developer
* machine. This is used in responses which return URLs to other endpoints,
* e.g. the debugger frontend and inspector proxy targets.
*
* Example: `'http://localhost:8081'`.
*/
serverBaseUrl: string,
logger?: Logger,
/**
* An interface for integrators to provide a custom implementation for
* opening URLs in a web browser.
*
* This is an unstable API with no semver guarantees.
*/
unstable_browserLauncher?: BrowserLauncher,
/**
* An interface for logging events.
*
* This is an unstable API with no semver guarantees.
*/
unstable_eventReporter?: EventReporter,
/**
* The set of experimental features to enable.
*
* This is an unstable API with no semver guarantees.
*/
unstable_experiments?: ExperimentsConfig,
/**
* Create custom handler to add support for unsupported CDP events, or debuggers.
* This handler is instantiated per logical device and debugger pair.
*
* This is an unstable API with no semver guarantees.
*/
unstable_customInspectorMessageHandler?: CreateCustomMessageHandlerFn,
}>;
type DevMiddlewareAPI = $ReadOnly<{
middleware: NextHandleFunction,
websocketEndpoints: {[path: string]: ws$WebSocketServer},
}>;
export default function createDevMiddleware({
projectRoot,
serverBaseUrl,
logger,
unstable_browserLauncher = DefaultBrowserLauncher,
unstable_eventReporter,
unstable_experiments: experimentConfig = {},
unstable_customInspectorMessageHandler,
}: Options): DevMiddlewareAPI {
const experiments = getExperiments(experimentConfig);
const inspectorProxy = new InspectorProxy(
projectRoot,
serverBaseUrl,
unstable_eventReporter,
experiments,
unstable_customInspectorMessageHandler,
);
const middleware = connect()
.use(
'/open-debugger',
experiments.enableNewDebugger
? openDebuggerMiddleware({
serverBaseUrl,
inspectorProxy,
browserLauncher: unstable_browserLauncher,
eventReporter: unstable_eventReporter,
experiments,
logger,
})
: deprecated_openFlipperMiddleware({
logger,
}),
)
.use(
'/debugger-frontend',
serveStaticMiddleware(path.join(reactNativeDebuggerFrontendPath), {
fallthrough: false,
}),
)
.use((...args) => inspectorProxy.processRequest(...args));
return {
middleware,
websocketEndpoints: inspectorProxy.createWebSocketListeners(),
};
}
function getExperiments(config: ExperimentsConfig): Experiments {
return {
enableNewDebugger: config.enableNewDebugger ?? false,
enableOpenDebuggerRedirect: config.enableOpenDebuggerRedirect ?? false,
enableNetworkInspector: config.enableNetworkInspector ?? false,
};
}