/** * 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. * * @providesModule setupDevtools * @flow */ 'use strict'; var Platform = require('Platform'); var NativeModules = require('NativeModules'); function setupDevtools() { var messageListeners = []; var closeListeners = []; var hostname = 'localhost'; if (Platform.OS === 'android' && NativeModules.AndroidConstants) { hostname = NativeModules.AndroidConstants.ServerHost.split(':')[0]; } var ws = new window.WebSocket('ws://' + hostname + ':8097/devtools'); // this is accessed by the eval'd backend code var FOR_BACKEND = { // eslint-disable-line no-unused-vars resolveRNStyle: require('flattenStyle'), wall: { listen(fn) { messageListeners.push(fn); }, onClose(fn) { closeListeners.push(fn); }, send(data) { ws.send(JSON.stringify(data)); }, }, }; ws.onclose = handleClose; ws.onerror = handleClose; ws.onopen = function () { tryToConnect(); }; var hasClosed = false; function handleClose() { if (!hasClosed) { hasClosed = true; setTimeout(setupDevtools, 2000); closeListeners.forEach(fn => fn()); } } function tryToConnect() { ws.send('attach:agent'); var _interval = setInterval(() => ws.send('attach:agent'), 500); ws.onmessage = evt => { if (evt.data.indexOf('eval:') === 0) { clearInterval(_interval); 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); return; } // This is breaking encapsulation of the React package. Move plz. var ReactNativeComponentTree = require('react/lib/ReactNativeComponentTree'); window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ ComponentTree: { getClosestInstanceFromNode: function (node) { return ReactNativeComponentTree.getClosestInstanceFromNode(node); }, getNodeFromInstance: function (inst) { // inst is an internal instance (but could be a composite) while (inst._renderedComponent) { inst = inst._renderedComponent; } if (inst) { return ReactNativeComponentTree.getNodeFromInstance(inst); } else { return null; } } }, Mount: require('react/lib/ReactNativeMount'), Reconciler: require('react/lib/ReactReconciler') }); ws.onmessage = handleMessage; } function handleMessage(evt) { // It's hard to handle JSON in a safe manner without inspecting it at // runtime, hence the any var data: any; try { data = JSON.parse(evt.data); } catch (e) { return console.error('failed to parse json: ' + evt.data); } // 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 = setupDevtools;