New NPM package react-devtools-inline (#363)
This commit is contained in:
parent
6f1e283b76
commit
dc8580e64d
|
@ -5,6 +5,7 @@ shells/browser/firefox/build
|
||||||
shells/browser/shared/build
|
shells/browser/shared/build
|
||||||
shells/dev/dist
|
shells/dev/dist
|
||||||
packages/react-devtools-core/dist
|
packages/react-devtools-core/dist
|
||||||
|
packages/react-devtools-inline/dist
|
||||||
vendor
|
vendor
|
||||||
*.js.snap
|
*.js.snap
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
/shells/browser/firefox/*.pem
|
/shells/browser/firefox/*.pem
|
||||||
/shells/browser/shared/build
|
/shells/browser/shared/build
|
||||||
/packages/react-devtools-core/dist
|
/packages/react-devtools-core/dist
|
||||||
|
/packages/react-devtools-inline/dist
|
||||||
/shells/dev/dist
|
/shells/dev/dist
|
||||||
build
|
build
|
||||||
/node_modules
|
/node_modules
|
||||||
/packages/react-devtools-core/node_modules/
|
/packages/react-devtools-core/node_modules
|
||||||
/packages/react-devtools/node_modules/
|
/packages/react-devtools-inline/node_modules
|
||||||
|
/packages/react-devtools/node_modules
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:core:backend": "cd ./packages/react-devtools-core && yarn build:backend",
|
"build:core:backend": "cd ./packages/react-devtools-core && yarn build:backend",
|
||||||
"build:core:standalone": "cd ./packages/react-devtools-core && yarn build:standalone",
|
"build:core:standalone": "cd ./packages/react-devtools-core && yarn build:standalone",
|
||||||
|
"build:core": "cd ./packages/react-devtools-core && yarn build",
|
||||||
|
"build:inline": "cd ./packages/react-devtools-inline && yarn build",
|
||||||
"build:demo": "cd ./shells/dev && cross-env NODE_ENV=development cross-env TARGET=remote webpack --config webpack.config.js",
|
"build:demo": "cd ./shells/dev && cross-env NODE_ENV=development cross-env TARGET=remote webpack --config webpack.config.js",
|
||||||
"build:extension": "cross-env NODE_ENV=production yarn run build:extension:chrome && yarn run build:extension:firefox",
|
"build:extension": "cross-env NODE_ENV=production yarn run build:extension:chrome && yarn run build:extension:firefox",
|
||||||
"build:extension:dev": "cross-env NODE_ENV=development yarn run build:extension:chrome && yarn run build:extension:firefox",
|
"build:extension:dev": "cross-env NODE_ENV=development yarn run build:extension:chrome && yarn run build:extension:firefox",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "react-devtools-core",
|
"name": "react-devtools-core",
|
||||||
"version": "4.0.0-alpha.6",
|
"version": "4.0.0-alpha.7",
|
||||||
"description": "Use react-devtools outside of the browser",
|
"description": "Use react-devtools outside of the browser",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "./dist/backend.js",
|
"main": "./dist/backend.js",
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
# `react-devtools-inline`
|
||||||
|
|
||||||
|
React DevTools implementation for embedding within a browser-based IDE (e.g. [CodeSandbox](https://codesandbox.io/), [StackBlitz](https://stackblitz.com/)).
|
||||||
|
|
||||||
|
This is a low-level package. If you're looking for the standalone DevTools app, **use the `react-devtools` package instead.**
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This package exports two entry points: a frontend (to be run in the main `window`) and a backend (to be installed and run within an `iframe`<sup>1</sup>).
|
||||||
|
|
||||||
|
The frontend and backend can be initialized in any order, but **the backend must not be activated until after the frontend has been initialized**. Because of this, the simplest sequence is:
|
||||||
|
|
||||||
|
1. Frontend (DevTools interface) initialized in the main `window`.
|
||||||
|
1. Backend initialized in an `iframe`.
|
||||||
|
1. Backend activated.
|
||||||
|
|
||||||
|
<sup>1</sup> Sandboxed iframes are supported.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### `react-devtools-inline/backend`
|
||||||
|
|
||||||
|
* **`initialize(contentWindow)`** -
|
||||||
|
Installs the global hook on the window. This hook is how React and DevTools communicate. **This method must be called before React is loaded.** (This means before any `import` or `require` statements!)
|
||||||
|
* **`activate(contentWindow)`** -
|
||||||
|
Lets the backend know when the frontend is ready. It should not be called until after the frontend has been initialized, else the frontend might miss important tree-initialization events.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { activate, initialize } from 'react-devtools-inline/backend';
|
||||||
|
|
||||||
|
// Call this before importing React (or any other packages that might import React).
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
// Call this only once the frontend has been initialized.
|
||||||
|
activate();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `react-devtools-inline/frontend`
|
||||||
|
|
||||||
|
* **`initialize(contentWindow)`** -
|
||||||
|
Configures the DevTools interface to listen to the `window` the backend was injected into. This method returns a React component that can be rendered directly.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { initialize } from 'react-devtools-inline/frontend';
|
||||||
|
|
||||||
|
// This should be the iframe the backend hook has been installed in.
|
||||||
|
const iframe = document.getElementById(frameID);
|
||||||
|
const contentWindow = iframe.contentWindow;
|
||||||
|
|
||||||
|
// This returns a React component that can be rendered into your app.
|
||||||
|
// <DevTools {...props} />
|
||||||
|
const DevTools = initialize(contentWindow);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
### Configuring a same-origin `iframe`
|
||||||
|
|
||||||
|
The simplest way to use this package is to install the hook from the parent `window`. This is possible if the `iframe` is not sandboxed and there are no cross-origin restrictions.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import {
|
||||||
|
activate as activateBackend,
|
||||||
|
initialize as initializeBackend
|
||||||
|
} from 'react-devtools-inline/backend';
|
||||||
|
import { initialize as initializeFrontend } from 'react-devtools-inline/frontend';
|
||||||
|
|
||||||
|
// The React app you want to inspect with DevTools is running within this iframe:
|
||||||
|
const iframe = document.getElementById('target');
|
||||||
|
const { contentWindow } = iframe;
|
||||||
|
|
||||||
|
// Installs the global hook into the iframe.
|
||||||
|
// This be called before React is loaded into that frame.
|
||||||
|
initializeBackend(contentWindow);
|
||||||
|
|
||||||
|
// React application can be injected into <iframe> at any time now...
|
||||||
|
|
||||||
|
// Initialize DevTools UI to listen to the hook we just installed.
|
||||||
|
// This returns a React component we can render anywhere in the parent window.
|
||||||
|
const DevTools = initializeFrontend(contentWindow);
|
||||||
|
|
||||||
|
// <DevTools /> interface can be rendered in the parent window at any time now...
|
||||||
|
|
||||||
|
// Let the backend know the frontend is ready and listening.
|
||||||
|
activateBackend(contentWindow);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuring a sandboxed `iframe`
|
||||||
|
|
||||||
|
Sandboxed `iframe`s are also supported but require more complex initialization.
|
||||||
|
|
||||||
|
**`iframe.html`**
|
||||||
|
```js
|
||||||
|
import { activate, initialize } from 'react-devtools-inline/backend';
|
||||||
|
|
||||||
|
// The DevTooks hook needs to be installed before React is even required!
|
||||||
|
// The safest way to do this is probably to install it in a separate script tag.
|
||||||
|
initialize(window);
|
||||||
|
|
||||||
|
// Wait for the frontend to let us know that it's ready.
|
||||||
|
window.addEventListener('message', ({ data }) => {
|
||||||
|
switch (data.type) {
|
||||||
|
case 'activate':
|
||||||
|
activate(window);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**`main-window.html`**
|
||||||
|
```js
|
||||||
|
import { initialize } from 'react-devtools-inline/frontend';
|
||||||
|
|
||||||
|
const iframe = document.getElementById('target');
|
||||||
|
const { contentWindow } = iframe;
|
||||||
|
|
||||||
|
// Initialize DevTools UI to listen to the iframe.
|
||||||
|
// This returns a React component we can render anywhere in the main window.
|
||||||
|
const DevTools = initialize(contentWindow);
|
||||||
|
|
||||||
|
// Let the backend know to initialize itself.
|
||||||
|
// We can't do this directly because the iframe is sandboxed.
|
||||||
|
// Only initialize the backend once the DevTools frontend has been initialized.
|
||||||
|
iframe.onload = () => {
|
||||||
|
contentWindow.postMessage(
|
||||||
|
{
|
||||||
|
type: 'activate',
|
||||||
|
},
|
||||||
|
'*'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
```
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./dist/backend');
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./dist/frontend');
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "react-devtools-inline",
|
||||||
|
"version": "4.0.0-alpha.7",
|
||||||
|
"description": "Embed react-devtools within a website",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "./dist/backend.js",
|
||||||
|
"repository": {
|
||||||
|
"url": "https://github.com/bvaughn/react-devtools-experimental.git",
|
||||||
|
"type": "git"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"backend.js",
|
||||||
|
"frontend.js"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
|
||||||
|
"prepublish": "yarn run build",
|
||||||
|
"start": "cross-env NODE_ENV=development webpack --config webpack.config.js --watch"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"devDependencies": {
|
||||||
|
"cross-env": "^3.1.4"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/** @flow */
|
||||||
|
|
||||||
|
import Agent from 'src/backend/agent';
|
||||||
|
import Bridge from 'src/bridge';
|
||||||
|
import { initBackend } from 'src/backend';
|
||||||
|
import { installHook } from 'src/hook';
|
||||||
|
import setupNativeStyleEditor from 'src/backend/NativeStyleEditor/setupNativeStyleEditor';
|
||||||
|
import {
|
||||||
|
MESSAGE_TYPE_GET_SAVED_PREFERENCES,
|
||||||
|
MESSAGE_TYPE_SAVED_PREFERENCES,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
function startActivation(contentWindow: window) {
|
||||||
|
const { parent } = contentWindow;
|
||||||
|
|
||||||
|
const onMessage = ({ data }) => {
|
||||||
|
switch (data.type) {
|
||||||
|
case MESSAGE_TYPE_SAVED_PREFERENCES:
|
||||||
|
// This is the only message we're listening for,
|
||||||
|
// so it's safe to cleanup after we've received it.
|
||||||
|
contentWindow.removeEventListener('message', onMessage);
|
||||||
|
|
||||||
|
const { appendComponentStack, componentFilters } = data;
|
||||||
|
|
||||||
|
contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack;
|
||||||
|
contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters;
|
||||||
|
|
||||||
|
// TRICKY
|
||||||
|
// The backend entry point may be required in the context of an iframe or the parent window.
|
||||||
|
// If it's required within the parent window, store the saved values on it as well,
|
||||||
|
// since the injected renderer interface will read from window.
|
||||||
|
// Technically we don't need to store them on the contentWindow in this case,
|
||||||
|
// but it doesn't really hurt anything to store them there too.
|
||||||
|
if (contentWindow !== window) {
|
||||||
|
window.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = appendComponentStack;
|
||||||
|
window.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = componentFilters;
|
||||||
|
}
|
||||||
|
|
||||||
|
finishActivation(contentWindow);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
contentWindow.addEventListener('message', onMessage);
|
||||||
|
|
||||||
|
// The backend may be unable to read saved preferences directly,
|
||||||
|
// because they are stored in localStorage within the context of the extension (on the frontend).
|
||||||
|
// Instead it relies on the extension to pass preferences through.
|
||||||
|
// Because we might be in a sandboxed iframe, we have to ask for them by way of postMessage().
|
||||||
|
parent.postMessage({ type: MESSAGE_TYPE_GET_SAVED_PREFERENCES }, '*');
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishActivation(contentWindow: window) {
|
||||||
|
const { parent } = contentWindow;
|
||||||
|
|
||||||
|
const bridge = new Bridge({
|
||||||
|
listen(fn) {
|
||||||
|
const onMessage = event => {
|
||||||
|
fn(event.data);
|
||||||
|
};
|
||||||
|
contentWindow.addEventListener('message', onMessage);
|
||||||
|
return () => {
|
||||||
|
contentWindow.removeEventListener('message', onMessage);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
send(event: string, payload: any, transferable?: Array<any>) {
|
||||||
|
parent.postMessage({ event, payload }, '*', transferable);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = new Agent(bridge);
|
||||||
|
|
||||||
|
const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||||
|
|
||||||
|
initBackend(hook, agent, contentWindow);
|
||||||
|
|
||||||
|
// Setup React Native style editor if a renderer like react-native-web has injected it.
|
||||||
|
if (!!hook.resolveRNStyle) {
|
||||||
|
setupNativeStyleEditor(
|
||||||
|
bridge,
|
||||||
|
agent,
|
||||||
|
hook.resolveRNStyle,
|
||||||
|
hook.nativeStyleEditorValidAttributes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function activate(contentWindow: window): void {
|
||||||
|
startActivation(contentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function initialize(contentWindow: window): void {
|
||||||
|
installHook(contentWindow);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
/** @flow */
|
||||||
|
|
||||||
|
export const MESSAGE_TYPE_GET_SAVED_PREFERENCES =
|
||||||
|
'React::DevTools::getSavedPreferences';
|
||||||
|
export const MESSAGE_TYPE_SAVED_PREFERENCES =
|
||||||
|
'React::DevTools::savedPreferences';
|
|
@ -0,0 +1,68 @@
|
||||||
|
/** @flow */
|
||||||
|
|
||||||
|
import React, { forwardRef } from 'react';
|
||||||
|
import Bridge from 'src/bridge';
|
||||||
|
import Store from 'src/devtools/store';
|
||||||
|
import DevTools from 'src/devtools/views/DevTools';
|
||||||
|
import { getSavedComponentFilters, getAppendComponentStack } from 'src/utils';
|
||||||
|
import {
|
||||||
|
MESSAGE_TYPE_GET_SAVED_PREFERENCES,
|
||||||
|
MESSAGE_TYPE_SAVED_PREFERENCES,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
|
import type { FrontendBridge } from 'src/bridge';
|
||||||
|
import type { Props } from 'src/devtools/views/DevTools';
|
||||||
|
|
||||||
|
export function initialize(
|
||||||
|
contentWindow: window
|
||||||
|
): React$AbstractComponent<Props, mixed> {
|
||||||
|
const onMessage = ({ data, origin, source }) => {
|
||||||
|
switch (data.type) {
|
||||||
|
case MESSAGE_TYPE_GET_SAVED_PREFERENCES:
|
||||||
|
// This is the only message we're listening for,
|
||||||
|
// so it's safe to cleanup after we've received it.
|
||||||
|
window.removeEventListener('message', onMessage);
|
||||||
|
|
||||||
|
// The renderer interface can't read saved preferences directly,
|
||||||
|
// because they are stored in localStorage within the context of the extension.
|
||||||
|
// Instead it relies on the extension to pass them through.
|
||||||
|
contentWindow.postMessage(
|
||||||
|
{
|
||||||
|
type: MESSAGE_TYPE_SAVED_PREFERENCES,
|
||||||
|
appendComponentStack: getAppendComponentStack(),
|
||||||
|
componentFilters: getSavedComponentFilters(),
|
||||||
|
},
|
||||||
|
'*'
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('message', onMessage);
|
||||||
|
|
||||||
|
const bridge: FrontendBridge = new Bridge({
|
||||||
|
listen(fn) {
|
||||||
|
const onMessage = ({ data }) => {
|
||||||
|
fn(data);
|
||||||
|
};
|
||||||
|
window.addEventListener('message', onMessage);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('message', onMessage);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
send(event: string, payload: any, transferable?: Array<any>) {
|
||||||
|
contentWindow.postMessage({ event, payload }, '*', transferable);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const store: Store = new Store(bridge);
|
||||||
|
|
||||||
|
const ForwardRef = forwardRef<Props, mixed>((props, ref) => (
|
||||||
|
<DevTools ref={ref} bridge={bridge} store={store} {...props} />
|
||||||
|
));
|
||||||
|
ForwardRef.displayName = 'DevTools';
|
||||||
|
|
||||||
|
return ForwardRef;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
const { resolve } = require('path');
|
||||||
|
const { DefinePlugin } = require('webpack');
|
||||||
|
const { getGitHubURL, getVersionString } = require('../../shells/utils');
|
||||||
|
|
||||||
|
const NODE_ENV = process.env.NODE_ENV;
|
||||||
|
if (!NODE_ENV) {
|
||||||
|
console.error('NODE_ENV not set');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const __DEV__ = true; // NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const GITHUB_URL = getGitHubURL();
|
||||||
|
const DEVTOOLS_VERSION = getVersionString();
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: __DEV__ ? 'development' : 'production',
|
||||||
|
devtool: false,
|
||||||
|
entry: {
|
||||||
|
backend: './src/backend.js',
|
||||||
|
frontend: './src/frontend.js',
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
path: __dirname + '/dist',
|
||||||
|
filename: '[name].js',
|
||||||
|
library: '[name]',
|
||||||
|
libraryTarget: 'commonjs2',
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
src: resolve(__dirname, '../../src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
externals: {
|
||||||
|
react: 'react',
|
||||||
|
'react-dom': 'react-dom',
|
||||||
|
scheduler: 'scheduler',
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new DefinePlugin({
|
||||||
|
__DEV__: __DEV__,
|
||||||
|
'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
|
||||||
|
'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
|
||||||
|
'process.env.NODE_ENV': `"${NODE_ENV}"`,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
options: {
|
||||||
|
configFile: resolve(__dirname, '../../babel.config.js'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'style-loader',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
loader: 'css-loader',
|
||||||
|
options: {
|
||||||
|
sourceMap: true,
|
||||||
|
modules: true,
|
||||||
|
localIdentName: '[local]___[hash:base64:5]',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "react-devtools",
|
"name": "react-devtools",
|
||||||
"version": "4.0.0-alpha.6",
|
"version": "4.0.0-alpha.7",
|
||||||
"description": "Use react-devtools outside of the browser",
|
"description": "Use react-devtools outside of the browser",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
"electron": "^5.0.0",
|
"electron": "^5.0.0",
|
||||||
"ip": "^1.1.4",
|
"ip": "^1.1.4",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"react-devtools-core": "4.0.0-alpha.6",
|
"react-devtools-core": "4.0.0-alpha.7",
|
||||||
"update-notifier": "^2.1.0"
|
"update-notifier": "^2.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,6 @@ function createPanelIfReactLoaded() {
|
||||||
|
|
||||||
let componentsPortalContainer = null;
|
let componentsPortalContainer = null;
|
||||||
let profilerPortalContainer = null;
|
let profilerPortalContainer = null;
|
||||||
let settingsPortalContainer = null;
|
|
||||||
|
|
||||||
let cloneStyleTags = null;
|
let cloneStyleTags = null;
|
||||||
let mostRecentOverrideTab = null;
|
let mostRecentOverrideTab = null;
|
||||||
|
@ -142,7 +141,6 @@ function createPanelIfReactLoaded() {
|
||||||
componentsPortalContainer,
|
componentsPortalContainer,
|
||||||
overrideTab,
|
overrideTab,
|
||||||
profilerPortalContainer,
|
profilerPortalContainer,
|
||||||
settingsPortalContainer,
|
|
||||||
showTabBar: false,
|
showTabBar: false,
|
||||||
showWelcomeToTheNewDevToolsDialog: true,
|
showWelcomeToTheNewDevToolsDialog: true,
|
||||||
store,
|
store,
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
/** @flow */
|
|
||||||
|
|
||||||
import Agent from 'src/backend/agent';
|
|
||||||
import Bridge from 'src/bridge';
|
|
||||||
import { initBackend } from 'src/backend';
|
|
||||||
import setupNativeStyleEditor from 'src/backend/NativeStyleEditor/setupNativeStyleEditor';
|
|
||||||
|
|
||||||
const bridge = new Bridge({
|
|
||||||
listen(fn) {
|
|
||||||
const listener = event => {
|
|
||||||
fn(event.data);
|
|
||||||
};
|
|
||||||
window.addEventListener('message', listener);
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('message', listener);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
send(event: string, payload: any, transferable?: Array<any>) {
|
|
||||||
window.parent.postMessage({ event, payload }, '*', transferable);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const agent = new Agent(bridge);
|
|
||||||
|
|
||||||
const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
||||||
|
|
||||||
initBackend(hook, agent, window.parent);
|
|
||||||
|
|
||||||
// Setup React Native style editor if a renderer like react-native-web has injected it.
|
|
||||||
if (!!hook.resolveRNStyle) {
|
|
||||||
setupNativeStyleEditor(
|
|
||||||
bridge,
|
|
||||||
agent,
|
|
||||||
hook.resolveRNStyle,
|
|
||||||
hook.nativeStyleEditorValidAttributes
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -3,24 +3,21 @@
|
||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
// $FlowFixMe Flow does not yet know about createRoot()
|
// $FlowFixMe Flow does not yet know about createRoot()
|
||||||
import { unstable_createRoot as createRoot } from 'react-dom';
|
import { unstable_createRoot as createRoot } from 'react-dom';
|
||||||
import Bridge from 'src/bridge';
|
import {
|
||||||
import { installHook } from 'src/hook';
|
activate as activateBackend,
|
||||||
|
initialize as initializeBackend,
|
||||||
|
} from 'react-devtools-inline/backend';
|
||||||
|
import { initialize as initializeFrontend } from 'react-devtools-inline/frontend';
|
||||||
import { initDevTools } from 'src/devtools';
|
import { initDevTools } from 'src/devtools';
|
||||||
import Store from 'src/devtools/store';
|
|
||||||
import DevTools from 'src/devtools/views/DevTools';
|
|
||||||
import { getSavedComponentFilters, getAppendComponentStack } from 'src/utils';
|
|
||||||
|
|
||||||
const iframe = ((document.getElementById('target'): any): HTMLIFrameElement);
|
const iframe = ((document.getElementById('target'): any): HTMLIFrameElement);
|
||||||
|
|
||||||
const { contentDocument, contentWindow } = iframe;
|
const { contentDocument, contentWindow } = iframe;
|
||||||
|
|
||||||
// The renderer interface can't read saved component filters directly,
|
// Helps with positioning Overlay UI.
|
||||||
// because they are stored in localStorage within the context of the extension.
|
contentWindow.__REACT_DEVTOOLS_TARGET_WINDOW__ = window;
|
||||||
// Instead it relies on the extension to pass filters through.
|
|
||||||
contentWindow.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = getSavedComponentFilters();
|
|
||||||
contentWindow.__REACT_DEVTOOLS_APPEND_COMPONENT_STACK__ = getAppendComponentStack();
|
|
||||||
|
|
||||||
installHook(contentWindow);
|
initializeBackend(contentWindow);
|
||||||
|
|
||||||
const container = ((document.getElementById('devtools'): any): HTMLElement);
|
const container = ((document.getElementById('devtools'): any): HTMLElement);
|
||||||
|
|
||||||
|
@ -48,46 +45,21 @@ mountButton.addEventListener('click', function() {
|
||||||
inject('dist/app.js', () => {
|
inject('dist/app.js', () => {
|
||||||
initDevTools({
|
initDevTools({
|
||||||
connect(cb) {
|
connect(cb) {
|
||||||
const bridge = new Bridge({
|
const DevTools = initializeFrontend(contentWindow);
|
||||||
listen(fn) {
|
|
||||||
const listener = ({ data }) => {
|
|
||||||
fn(data);
|
|
||||||
};
|
|
||||||
// Preserve the reference to the window we subscribe to, so we can unsubscribe from it when required.
|
|
||||||
const contentWindowParent = contentWindow.parent;
|
|
||||||
contentWindowParent.addEventListener('message', listener);
|
|
||||||
return () => {
|
|
||||||
contentWindowParent.removeEventListener('message', listener);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
send(event: string, payload: any, transferable?: Array<any>) {
|
|
||||||
contentWindow.postMessage({ event, payload }, '*', transferable);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
cb(bridge);
|
// Activate the backend only once the DevTools frontend Store has been initialized.
|
||||||
|
// Otherwise the Store may miss important initial tree op codes.
|
||||||
const store = new Store(bridge);
|
activateBackend(contentWindow);
|
||||||
|
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
const batch = root.createBatch();
|
root.render(
|
||||||
batch.render(
|
|
||||||
createElement(DevTools, {
|
createElement(DevTools, {
|
||||||
bridge,
|
|
||||||
browserTheme: 'light',
|
browserTheme: 'light',
|
||||||
showTabBar: true,
|
showTabBar: true,
|
||||||
showWelcomeToTheNewDevToolsDialog: true,
|
showWelcomeToTheNewDevToolsDialog: true,
|
||||||
store,
|
|
||||||
warnIfLegacyBackendDetected: true,
|
warnIfLegacyBackendDetected: true,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
batch.then(() => {
|
|
||||||
batch.commit();
|
|
||||||
|
|
||||||
// Initialize the backend only once the DevTools frontend Store has been initialized.
|
|
||||||
// Otherwise the Store may miss important initial tree op codes.
|
|
||||||
inject('dist/backend.js');
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onReload(reloadFn) {
|
onReload(reloadFn) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ if (!TARGET) {
|
||||||
|
|
||||||
const __DEV__ = NODE_ENV === 'development';
|
const __DEV__ = NODE_ENV === 'development';
|
||||||
|
|
||||||
|
const root = resolve(__dirname, '../..');
|
||||||
|
|
||||||
const GITHUB_URL = getGitHubURL();
|
const GITHUB_URL = getGitHubURL();
|
||||||
const DEVTOOLS_VERSION = getVersionString();
|
const DEVTOOLS_VERSION = getVersionString();
|
||||||
|
|
||||||
|
@ -24,12 +26,15 @@ const config = {
|
||||||
devtool: false,
|
devtool: false,
|
||||||
entry: {
|
entry: {
|
||||||
app: './app/index.js',
|
app: './app/index.js',
|
||||||
backend: './src/backend.js',
|
|
||||||
devtools: './src/devtools.js',
|
devtools: './src/devtools.js',
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
src: resolve(__dirname, '../../src'),
|
'react-devtools-inline': resolve(
|
||||||
|
root,
|
||||||
|
'packages/react-devtools-inline/src/'
|
||||||
|
),
|
||||||
|
src: resolve(root, 'src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
@ -45,7 +50,7 @@ const config = {
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
loader: 'babel-loader',
|
loader: 'babel-loader',
|
||||||
options: {
|
options: {
|
||||||
configFile: require.resolve('../../babel.config.js'),
|
configFile: resolve(root, 'babel.config.js'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -153,20 +153,11 @@ export default class Overlay {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Find the root window, because overlays are positioned relative to it.
|
// Find the root window, because overlays are positioned relative to it.
|
||||||
let currentWindow = window;
|
let currentWindow = window.__REACT_DEVTOOLS_TARGET_WINDOW__ || window;
|
||||||
while (currentWindow !== currentWindow.parent) {
|
|
||||||
currentWindow = currentWindow.parent;
|
|
||||||
}
|
|
||||||
this.window = currentWindow;
|
this.window = currentWindow;
|
||||||
|
|
||||||
// When opened in shells/dev, the tooltip should be bound by the app iframe, not by the topmost window.
|
// When opened in shells/dev, the tooltip should be bound by the app iframe, not by the topmost window.
|
||||||
let tipBoundsWindow = window;
|
let tipBoundsWindow = window.__REACT_DEVTOOLS_TARGET_WINDOW__ || window;
|
||||||
while (
|
|
||||||
tipBoundsWindow !== tipBoundsWindow.parent &&
|
|
||||||
!tipBoundsWindow.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')
|
|
||||||
) {
|
|
||||||
tipBoundsWindow = tipBoundsWindow.parent;
|
|
||||||
}
|
|
||||||
this.tipBoundsWindow = tipBoundsWindow;
|
this.tipBoundsWindow = tipBoundsWindow;
|
||||||
|
|
||||||
const doc = currentWindow.document;
|
const doc = currentWindow.document;
|
||||||
|
|
|
@ -114,7 +114,7 @@ export default class Store extends EventEmitter<{|
|
||||||
|
|
||||||
// These options may be initially set by a confiugraiton option when constructing the Store.
|
// These options may be initially set by a confiugraiton option when constructing the Store.
|
||||||
// In the case of "supportsProfiling", the option may be updated based on the injected renderers.
|
// In the case of "supportsProfiling", the option may be updated based on the injected renderers.
|
||||||
_supportsNativeInspection: boolean = false;
|
_supportsNativeInspection: boolean = true;
|
||||||
_supportsProfiling: boolean = false;
|
_supportsProfiling: boolean = false;
|
||||||
_supportsReloadAndProfile: boolean = false;
|
_supportsReloadAndProfile: boolean = false;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,6 @@ export type Props = {|
|
||||||
// but individual tabs (e.g. Components, Profiling) can be rendered into portals within their browser panels.
|
// but individual tabs (e.g. Components, Profiling) can be rendered into portals within their browser panels.
|
||||||
componentsPortalContainer?: Element,
|
componentsPortalContainer?: Element,
|
||||||
profilerPortalContainer?: Element,
|
profilerPortalContainer?: Element,
|
||||||
settingsPortalContainer?: Element,
|
|
||||||
|};
|
|};
|
||||||
|
|
||||||
const componentsTab = {
|
const componentsTab = {
|
||||||
|
@ -85,7 +84,6 @@ export default function DevTools({
|
||||||
componentsPortalContainer,
|
componentsPortalContainer,
|
||||||
overrideTab,
|
overrideTab,
|
||||||
profilerPortalContainer,
|
profilerPortalContainer,
|
||||||
settingsPortalContainer,
|
|
||||||
showTabBar = false,
|
showTabBar = false,
|
||||||
showWelcomeToTheNewDevToolsDialog = false,
|
showWelcomeToTheNewDevToolsDialog = false,
|
||||||
store,
|
store,
|
||||||
|
@ -113,7 +111,6 @@ export default function DevTools({
|
||||||
browserTheme={browserTheme}
|
browserTheme={browserTheme}
|
||||||
componentsPortalContainer={componentsPortalContainer}
|
componentsPortalContainer={componentsPortalContainer}
|
||||||
profilerPortalContainer={profilerPortalContainer}
|
profilerPortalContainer={profilerPortalContainer}
|
||||||
settingsPortalContainer={settingsPortalContainer}
|
|
||||||
>
|
>
|
||||||
<ViewElementSourceContext.Provider value={viewElementSource}>
|
<ViewElementSourceContext.Provider value={viewElementSource}>
|
||||||
<TreeContextController>
|
<TreeContextController>
|
||||||
|
|
|
@ -41,7 +41,6 @@ type Props = {|
|
||||||
children: React$Node,
|
children: React$Node,
|
||||||
componentsPortalContainer?: Element,
|
componentsPortalContainer?: Element,
|
||||||
profilerPortalContainer?: Element,
|
profilerPortalContainer?: Element,
|
||||||
settingsPortalContainer?: Element,
|
|
||||||
|};
|
|};
|
||||||
|
|
||||||
function SettingsContextController({
|
function SettingsContextController({
|
||||||
|
@ -49,7 +48,6 @@ function SettingsContextController({
|
||||||
children,
|
children,
|
||||||
componentsPortalContainer,
|
componentsPortalContainer,
|
||||||
profilerPortalContainer,
|
profilerPortalContainer,
|
||||||
settingsPortalContainer,
|
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const bridge = useContext(BridgeContext);
|
const bridge = useContext(BridgeContext);
|
||||||
|
|
||||||
|
@ -82,18 +80,8 @@ function SettingsContextController({
|
||||||
.documentElement: any): HTMLElement)
|
.documentElement: any): HTMLElement)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (settingsPortalContainer != null) {
|
|
||||||
array.push(
|
|
||||||
((settingsPortalContainer.ownerDocument
|
|
||||||
.documentElement: any): HTMLElement)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return array;
|
return array;
|
||||||
}, [
|
}, [componentsPortalContainer, profilerPortalContainer]);
|
||||||
componentsPortalContainer,
|
|
||||||
profilerPortalContainer,
|
|
||||||
settingsPortalContainer,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const computedStyle = getComputedStyle((document.body: any));
|
const computedStyle = getComputedStyle((document.body: any));
|
||||||
const comfortableLineHeight = parseInt(
|
const comfortableLineHeight = parseInt(
|
||||||
|
|
Loading…
Reference in New Issue