Skip to content

Commit

Permalink
STASHING
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Dec 11, 2021
1 parent 555e871 commit 5f767f6
Show file tree
Hide file tree
Showing 18 changed files with 406 additions and 35 deletions.
2 changes: 1 addition & 1 deletion packages/react-devtools-inline/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"hookNames.js"
],
"scripts": {
"build": "cross-env NODE_ENV=production webpack --config webpack.config.js",
"build": "cross-env NODE_ENV=development webpack --config webpack.config.js",
"prepublish": "yarn run build",
"start": "cross-env NODE_ENV=development webpack --config webpack.config.js --watch",
"test:e2e": "playwright test --config=playwright.config.js"
Expand Down
68 changes: 46 additions & 22 deletions packages/react-devtools-inline/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
MESSAGE_TYPE_SAVED_PREFERENCES,
} from './constants';

function startActivation(contentWindow: window) {
import type {BackendBridge} from 'react-devtools-shared/src/bridge';
import type {Wall} from 'react-devtools-shared/src/types';

function startActivation(contentWindow: window, bridge: BackendBridge) {
const {parent} = contentWindow;

const onMessage = ({data}) => {
Expand Down Expand Up @@ -48,7 +51,7 @@ function startActivation(contentWindow: window) {
window.__REACT_DEVTOOLS_HIDE_CONSOLE_LOGS_IN_STRICT_MODE__ = hideConsoleLogsInStrictMode;
}

finishActivation(contentWindow);
finishActivation(contentWindow, bridge);
break;
default:
break;
Expand All @@ -61,27 +64,11 @@ function startActivation(contentWindow: window) {
// 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().
// TODO WHAT HUH
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);
},
});

function finishActivation(contentWindow: window, bridge: BackendBridge) {
const agent = new Agent(bridge);

const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
Expand All @@ -100,8 +87,45 @@ function finishActivation(contentWindow: window) {
}
}

export function activate(contentWindow: window): void {
startActivation(contentWindow);
export function activate(
contentWindow: window,
{
bridge,
}: {|
bridge?: BackendBridge,
|} = {},
): void {
if (bridge == null) {
bridge = createBridge(contentWindow);
}

startActivation(contentWindow, bridge);
}

export function createBridge(
contentWindow: window,
wall?: Wall,
): BackendBridge {
const {parent} = contentWindow;

if (wall == null) {
wall = {
listen(fn) {
const onMessage = ({data}) => {
fn(data);
};
window.addEventListener('message', onMessage);
return () => {
window.removeEventListener('message', onMessage);
};
},
send(event: string, payload: any, transferable?: Array<any>) {
parent.postMessage({event, payload}, '*', transferable);
},
};
}

return (new Bridge(wall): BackendBridge);
}

export function initialize(contentWindow: window): void {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-devtools-shared/src/backend/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ export default class Agent extends EventEmitter<{|
bridge.send('profilingStatus', true);
}

// Send the Bridge protocol after initialization in case the frontend has already requested it.
this._bridge.send('bridgeProtocol', currentBridgeProtocol);

// Notify the frontend if the backend supports the Storage API (e.g. localStorage).
// If not, features like reload-and-profile will not work correctly and must be disabled.
let isBackendStorageAPISupported = false;
Expand Down Expand Up @@ -320,6 +323,7 @@ export default class Agent extends EventEmitter<{|
}

getBridgeProtocol = () => {
console.log('[agent] getBridgeProtocol -> bridge.send("bridgeProtocol")');
this._bridge.send('bridgeProtocol', currentBridgeProtocol);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@
<!-- This script installs the hook, injects the backend, and renders the DevTools UI -->
<!-- In DEV mode, this file is served by the Webpack dev server -->
<!-- For production builds, it's built by Webpack and uploaded from the local file system -->
<script src="dist/devtools.js"></script>
<script src="dist/app-devtools.js"></script>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/react-devtools-shell/multi-left.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!doctype html>
<html>
<body>
<script src="dist/multi-left.js"></script>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/react-devtools-shell/multi-right.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!doctype html>
<html>
<body>
<script src="dist/multi-right.js"></script>
</body>
</html>
57 changes: 57 additions & 0 deletions packages/react-devtools-shell/multi.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<!doctype html>
<html>
<head>
<meta charset="utf8">
<title>React DevTools</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
* {
box-sizing: border-box;
}
body {
display: flex;
flex-direction: row;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
font-size: 12px;
line-height: 1.5;
}
.column {
display: flex;
flex-direction: column;
flex: 1 1 50%;
}
.column:first-of-type {
border-right: 1px solid #3d424a;
}
.iframe {
height: 50%;
flex: 0 0 50%;
border: none;
}
.devtools {
height: 50%;
flex: 0 0 50%;
}
</style>
</head>
<body>
<div class="column left-column">
<iframe src="multi-left.html" id="iframe-left" class="iframe"></iframe>
<div id="devtools-left" class="devtools"></div>
</div>
<div class="column">
<iframe src="multi-right.html" id="iframe-right" class="iframe"></iframe>
<div id="devtools-right" class="devtools"></div>
</div>

<script src="dist/multi-devtools.js"></script>
</body>
</html>
5 changes: 0 additions & 5 deletions packages/react-devtools-shell/now.json

This file was deleted.

6 changes: 3 additions & 3 deletions packages/react-devtools-shell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
"name": "react-devtools-shell",
"version": "0.0.0",
"scripts": {
"build": "cross-env NODE_ENV=development cross-env TARGET=remote webpack --config webpack.config.js",
"deploy": "yarn run build && now deploy && now alias react-devtools-experimental",
"start": "cross-env NODE_ENV=development cross-env TARGET=local webpack-dev-server --open"
"start": "yarn start:app",
"start:app": "cross-env NODE_ENV=development cross-env TARGET=local webpack-dev-server --open-page app.html",
"start:multi": "cross-env NODE_ENV=development cross-env TARGET=local webpack-dev-server --open-page multi.html"
},
"dependencies": {
"immutable": "^4.0.0-rc.12",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function hookNamesModuleLoaderFunction() {
return import('react-devtools-inline/hookNames');
}

inject('dist/app.js', () => {
inject('dist/app-index.js', () => {
initDevTools({
connect(cb) {
const root = createRoot(container);
Expand Down
71 changes: 71 additions & 0 deletions packages/react-devtools-shell/src/multi/devtools.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as React from 'react';
import {Component} from 'react';
import {createRoot} from 'react-dom';
import {
activate as activateBackend,
createBridge as createBackendBridge,
initialize as initializeBackend,
} from 'react-devtools-inline/backend';
import {
createBridge as createFrontendBridge,
createStore,
initialize as createDevTools,
} from 'react-devtools-inline/frontend';

function init(appIframe, devtoolsContainer) {
const contentWindow = appIframe.contentWindow;

// Wire each DevTools instance directly to its app.
// By default, DevTools dispatches "message" events on the window,
// but this means that only one instance of DevTools can live on a page.
const wall = {
_listeners: [],
emit() {},
listen(listener) {
wall._listeners.push(listener);
},
send(event, payload) {
wall._listeners.forEach(listener => listener({event, payload}));
},
};

const backendBridge = createBackendBridge(contentWindow, wall);

// TODO (sandpack) There is a race condition here.
// We don't want to initialize the backend too soon because the Store might miss tree operations.
// But we can't wait so late to initialize it without risking missing the "getBridgeProtocol" message.
// The best backwards-compatible fix would be to change the timing of when we ask for the bridge protocol?

initializeBackend(contentWindow);

// Call this only once the frontend has been initialized.
class InitializeBackendOnMount extends Component {
componentDidMount() {
activateBackend(contentWindow, {bridge: backendBridge});
}
render() {
return this.props.children;
}
}

const frontendBridge = createFrontendBridge(contentWindow, wall);
const store = createStore(frontendBridge);
const DevTools = createDevTools(contentWindow, {
bridge: frontendBridge,
store,
});

createRoot(devtoolsContainer).render(
<InitializeBackendOnMount>
<DevTools />
</InitializeBackendOnMount>,
);
}

const appIframeLeft = document.getElementById('iframe-left');
const appIframeRight = document.getElementById('iframe-right');
const devtoolsContainerLeft = document.getElementById('devtools-left');
const devtoolsContainerRight = document.getElementById('devtools-right');

init(appIframeLeft, devtoolsContainerLeft);
init(appIframeRight, devtoolsContainerRight);
19 changes: 19 additions & 0 deletions packages/react-devtools-shell/src/multi/left.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';
import {useState} from 'react';
import {createRoot} from 'react-dom';

function createContainer() {
const container = document.createElement('div');

((document.body: any): HTMLBodyElement).appendChild(container);

return container;
}

function StatefulCounter() {
const [count, setCount] = useState(0);
const handleClick = () => setCount(count + 1);
return <button onClick={handleClick}>Count {count}</button>;
}

createRoot(createContainer()).render(<StatefulCounter />);
33 changes: 33 additions & 0 deletions packages/react-devtools-shell/src/multi/right.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from 'react';
import {useLayoutEffect, useRef, useState} from 'react';
import {render} from 'react-dom';

function createContainer() {
const container = document.createElement('div');

((document.body: any): HTMLBodyElement).appendChild(container);

return container;
}

function EffectWithState() {
const [didMount, setDidMount] = useState(0);

const renderCountRef = useRef(0);
renderCountRef.current++;

useLayoutEffect(() => {
if (!didMount) {
setDidMount(true);
}
}, [didMount]);

return (
<ul>
<li>Rendered {renderCountRef.current} times</li>
{didMount && <li>Mounted!</li>}
</ul>
);
}

render(<EffectWithState />, createContainer());
19 changes: 19 additions & 0 deletions packages/react-devtools-shell/webpack.config-app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const baseConfig = require('./webpack.config-base.js');

module.exports = {
...baseConfig,
entry: {
app: './src/app/index.js',
'app-devtools': './src/app/devtools.js',
'multi-left': './src/multi/left.js',
'multi-devtools': './src/multi/devtools.js',
'multi-right': './src/multi/right.js',
},
devServer: {
hot: true,
port: 8080,
clientLogLevel: 'warning',
publicPath: '/dist/',
stats: 'errors-only',
},
};
Loading

0 comments on commit 5f767f6

Please sign in to comment.