diff --git a/Libraries/Devtools/setupDevtools.js b/Libraries/Devtools/setupDevtools.js new file mode 100644 index 000000000..0bd003dca --- /dev/null +++ b/Libraries/Devtools/setupDevtools.js @@ -0,0 +1,105 @@ +/** + * 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'; + +function setupDevtools() { + var messageListeners = []; + var closeListeners = []; + var ws = new window.WebSocket('ws://localhost:8081/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 = () => { + 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.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; + } + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({ + CurrentOwner: require('ReactCurrentOwner'), + InstanceHandles: require('ReactInstanceHandles'), + Mount: require('ReactNativeMount'), + Reconciler: require('ReactReconciler'), + TextComponent: require('ReactNativeTextComponent'), + }); + ws.onmessage = handleMessage; + } + + function handleMessage(evt) { + var data; + 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; diff --git a/Libraries/Inspector/Inspector.js b/Libraries/Inspector/Inspector.js index c014c29c4..7dbec7443 100644 --- a/Libraries/Inspector/Inspector.js +++ b/Libraries/Inspector/Inspector.js @@ -20,11 +20,9 @@ var StyleSheet = require('StyleSheet'); var UIManager = require('NativeModules').UIManager; var View = require('View'); -var REACT_DEVTOOLS_HOOK: ?Object = typeof window !== 'undefined' ? window.__REACT_DEVTOOLS_GLOBAL_HOOK__ : null; - -if (REACT_DEVTOOLS_HOOK) { +if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) { // required for devtools to be able to edit react native styles - REACT_DEVTOOLS_HOOK.resolveRNStyle = require('flattenStyle'); + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.resolveRNStyle = require('flattenStyle'); } class Inspector extends React.Component { @@ -43,12 +41,12 @@ class Inspector extends React.Component { } componentDidMount() { - if (REACT_DEVTOOLS_HOOK) { + if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) { this.attachToDevtools = this.attachToDevtools.bind(this); - REACT_DEVTOOLS_HOOK.on('react-devtools', this.attachToDevtools); + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('react-devtools', this.attachToDevtools); // if devtools is already started - if (REACT_DEVTOOLS_HOOK.reactDevtoolsAgent) { - this.attachToDevtools(REACT_DEVTOOLS_HOOK.reactDevtoolsAgent); + if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__.reactDevtoolsAgent) { + this.attachToDevtools(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.reactDevtoolsAgent); } } } @@ -57,8 +55,8 @@ class Inspector extends React.Component { if (this._subs) { this._subs.map(fn => fn()); } - if (REACT_DEVTOOLS_HOOK) { - REACT_DEVTOOLS_HOOK.off('react-devtools', this.attachToDevtools); + if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) { + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.off('react-devtools', this.attachToDevtools); } } diff --git a/Libraries/ReactIOS/renderApplication.ios.js b/Libraries/ReactIOS/renderApplication.ios.js index 53a786e2a..6a4bb5f68 100644 --- a/Libraries/ReactIOS/renderApplication.ios.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -69,6 +69,11 @@ function renderApplication( rootTag, 'Expect to have a valid rootTag, instead got ', rootTag ); + // not when debugging in chrome + if (__DEV__ && !window.document) { + var setupDevtools = require('setupDevtools'); + setupDevtools(); + } React.render(