Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[ignore]
.*/react/node_modules/.*
.*/electron/node_modules/.*
.*node_modules/babel.*
.*node_modules/classnames.*
.*node_modules/json-loader.*
Expand Down
95 changes: 95 additions & 0 deletions backend/websocketConnect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/
'use strict';

function websocketConnect(uri: string, WebSocket?: (val: string) => Object) {
WebSocket = WebSocket || window.WebSocket;
var messageListeners = [];
var closeListeners = [];
var ws = new WebSocket(uri);
// this is accessed by the eval'd backend code
var FOR_BACKEND = { // eslint-disable-line no-unused-vars
wall: {
listen(fn) {
messageListeners.push(fn);
},
onClose(fn) {
closeListeners.push(fn);
},
send(data) {
ws.send(JSON.stringify(data));
},
},
};
ws.onclose = () => {
console.warn('devtools socket closed');
closeListeners.forEach(fn => fn());
};
ws.onerror = error => {
console.warn('devtools socket errored', error);
closeListeners.forEach(fn => fn());
};
ws.onopen = function() {
tryToConnect();
};

function tryToConnect() {
ws.onmessage = evt => {
if (evt.data.indexOf('eval:') === 0) {
initialize(evt.data.slice('eval:'.length));
}
};
}

function initialize(text) {
try {
// FOR_BACKEND is used by the eval'd code
eval(text); // eslint-disable-line no-eval
} catch (e) {
console.error('Failed to eval' + e.message + '\n' + e.stack);
debugger; // eslint-disable-line no-debugger
return;
}
ws.onmessage = handleMessage;
}
function handleMessage(evt) {
var data;
try {
data = JSON.parse(evt.data);
} catch (e) {
console.error('failed to parse json: ' + evt.data);
return;
}
// the devtools closed
if (data.$close || data.$error) {
closeListeners.forEach(fn => fn());
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.emit('shutdown');
tryToConnect();
return;
}
if (data.$open) {
return; // ignore
}
messageListeners.forEach(fn => {
try {
fn(data);
} catch (e) {
// jsc doesn't play so well with tracebacks that go into eval'd code,
// so the stack trace here will stop at the `eval()` call. Getting the
// message that caused the error is the best we can do for now.
console.log(data);
throw e;
}
});
}
}

module.exports = websocketConnect;
41 changes: 38 additions & 3 deletions shells/electron/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,58 @@
white-space: pre;
}
#waiting {
flex-direction: column;
text-align: center;
font-family: sans-serif;
color: #aaa;
flex: 1;
}
#waiting h2 {
padding: 30px;
color: #aaa;
}
#waiting p {
padding: 0;
margin: 0;
}
#waiting input {
width: 300px;
font-family: monospace;
}
#loading-status {
margin-top: 10px;
}
</style>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<div id="container">
<h2 id="waiting">Waiting for a connection from React Native</h2>
<div id="waiting">
<h2>Waiting for a connection from React Native</h2>
<div>
<div>
Add <input id="localhost"><br/>
or <input id="byip">
</div>
<div>to the top of the page you want to debug,<br/>*before* react is loaded.</div>
<div id="loading-status">starting server</div>
</div>
</div>
</div>
<script>
var port = 8097;
var localIp = require('ip').address();
var $ = document.querySelector.bind(document);
function selectAll() {
this.selectionStart = 0;
this.selectionEnd = this.value.length;
}
$('#localhost').value = '<' + 'script src="http://localhost:' + port + '"><' + '/script>';
$('#byip').value = '<' + 'script src="http://' + localIp + ':' + port + '"><' + '/script>';
$('#localhost').onclick = selectAll;
$('#byip').onclick = selectAll;
window.server = require('./build/standalone.js')
.setContentDOMNode(document.getElementById('container'))
.startServer();
.startServer(port);
</script>
</body>
</html>
7 changes: 6 additions & 1 deletion shells/electron/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
{
"main": "./index.js",
"scripts": {
"build": "../../node_modules/.bin/webpack --config webpack.backend.js && ../../node_modules/.bin/webpack --config webpack.config.js"
"backend": "../../node_modules/.bin/webpack --config webpack.backend.js",
"ui": "../../node_modules/.bin/webpack --config webpack.config.js",
"ui:watch": "../../node_modules/.bin/webpack --config webpack.config.js --watch",
"build": "npm run backend && npm run ui",
"start": "electron ."
},
"dependencies": {
"electron-prebuilt": "^1.4.2",
"ws": "1.1.0"
}
}
21 changes: 21 additions & 0 deletions shells/electron/src/embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/
'use strict';

var globalHook = require('../../../backend/installGlobalHook');
globalHook(window);
var websocketConnect = require('../../../backend/websocketConnect');
var setupHighlighter = require('../../../frontend/Highlighter/setup');

websocketConnect('ws://localhost:8097/');
window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('react-devtools', agent => {
setupHighlighter(agent);
});
58 changes: 24 additions & 34 deletions shells/electron/src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
'use strict';

var ws = require('ws');
var fs = require('fs');
var path = require('path');

var installGlobalHook = require('../../../backend/installGlobalHook');
installGlobalHook(window);
Expand All @@ -18,7 +20,8 @@ var React = require('react');
var ReactDOM = require('react-dom');

var node = null;
var backendScript = require('raw!../build/backend.js');
var backendScript = fs.readFileSync(
path.join(__dirname, '../build/backend.js'));
var wall = null;

var config = {
Expand Down Expand Up @@ -93,39 +96,10 @@ function initialize(socket) {
reload();
}

/**
* This is the normal mode, where it connects to the react native packager
*/
function connectToSocket(port = 8081) {
var socket = ws.connect(`ws://localhost:${port}/devtools`);
socket.onmessage = (evt) => {
if (evt.data === 'attach:agent') {
initialize(socket);
}
};
socket.onerror = (err) => {
onDisconnected();
console.log('Error with websocket connection', err);
};
socket.onclose = () => {
onDisconnected();
console.log('Connection to RN closed');
};

return {
close: function() {
onDisconnected();
socket.close();
},
};
}

/**
* When the Electron app is running in "server mode"
*/
var restartTimeout = null;
function startServer(port = 8097) {
var server = new ws.Server({port});
var httpServer = require('http').createServer();
var server = new ws.Server({server: httpServer});
var connected = false;
server.on('connection', (socket) => {
if (connected) {
Expand Down Expand Up @@ -153,12 +127,29 @@ function startServer(port = 8097) {
restartTimeout = setTimeout(() => startServer(port), 1000);
});

var embedFile = fs.readFileSync(
path.join(__dirname, '../build/embed.js')
);
httpServer.on('request', (req, res) => {
res.end(embedFile);
});

httpServer.on('error', (e) => {
onError(e);
document.getElementById('loading-status').innerText = 'failed to start server :/';
restartTimeout = setTimeout(() => startServer(port), 1000);
});

httpServer.listen(port, () => {
document.getElementById('loading-status').innerText = 'listening on ' + port;
});

return {
close: function() {
connected = false;
onDisconnected();
clearTimeout(restartTimeout);
server.close();
httpServer.close();
},
};
}
Expand All @@ -170,7 +161,6 @@ var DevtoolsUI = {
},

startServer,
connectToSocket,
};

module.exports = DevtoolsUI;
1 change: 1 addition & 0 deletions shells/electron/webpack.backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
devtool: 'source-map',
entry: {
backend: './src/backend.js',
embed: './src/embed.js',
},
output: {
path: __dirname + '/build', // eslint-disable-line no-path-concat
Expand Down
6 changes: 6 additions & 0 deletions shells/electron/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ module.exports = {
entry: {
standalone: './src/ui.js',
},
// this lets us `require('fs')` etc
target: 'electron',
node: {
// don't replace __dirname, electron will handle it
__dirname: false,
},
output: {
path: __dirname + '/build', // eslint-disable-line no-path-concat
filename: '[name].js',
Expand Down