diff --git a/Libraries/Utilities/HMRClient.js b/Libraries/Utilities/HMRClient.js index 4934c4ff4283b8..27246a3482b85b 100644 --- a/Libraries/Utilities/HMRClient.js +++ b/Libraries/Utilities/HMRClient.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule HMRClient + * @format * @flow */ 'use strict'; @@ -14,6 +15,8 @@ const Platform = require('Platform'); const invariant = require('fbjs/lib/invariant'); +const MetroHMRClient = require('metro/src/lib/bundle-modules/HMRClient'); + /** * HMR Client that receives from the server HMR updates and propagates them * runtime to reflects those changes. @@ -24,110 +27,72 @@ const HMRClient = { invariant(bundleEntry, 'Missing required paramenter `bundleEntry`'); invariant(host, 'Missing required paramenter `host`'); - // need to require WebSocket inside of `enable` function because - // this module is defined as a `polyfillGlobal`. - // See `InitializeJavascriptAppEngine.js` - const WebSocket = require('WebSocket'); + // Moving to top gives errors due to NativeModules not being initialized + const HMRLoadingView = require('HMRLoadingView'); - const wsHostPort = port !== null && port !== '' - ? `${host}:${port}` - : host; + const wsHostPort = port !== null && port !== '' ? `${host}:${port}` : host; bundleEntry = bundleEntry.replace(/\.(bundle|delta)/, '.js'); // Build the websocket url - const wsUrl = `ws://${wsHostPort}/hot?` + + const wsUrl = + `ws://${wsHostPort}/hot?` + `platform=${platform}&` + `bundleEntry=${bundleEntry}`; - const activeWS = new WebSocket(wsUrl); - activeWS.onerror = (e) => { - let error = ( -`Hot loading isn't working because it cannot connect to the development server. + const hmrClient = new MetroHMRClient(wsUrl); + + hmrClient.on('connection-error', e => { + let error = `Hot loading isn't working because it cannot connect to the development server. Try the following to fix the issue: -- Ensure that the packager server is running and available on the same network` - ); +- Ensure that the packager server is running and available on the same network`; if (Platform.OS === 'ios') { - error += ( -` -- Ensure that the Packager server URL is correctly set in AppDelegate` - ); + error += ` +- Ensure that the Packager server URL is correctly set in AppDelegate`; } else { - error += ( -` + error += ` - Ensure that your device/emulator is connected to your machine and has USB debugging enabled - run 'adb devices' to see a list of connected devices - If you're on a physical device connected to the same machine, run 'adb reverse tcp:8081 tcp:8081' to forward requests from your device -- If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081` - ); +- If your device is on the same Wi-Fi network, set 'Debug server host & port for device' in 'Dev settings' to your machine's IP address and the port of the local dev server - e.g. 10.0.1.1:8081`; } - error += ( -` + error += ` URL: ${host}:${port} -Error: ${e.message}` - ); +Error: ${e.message}`; throw new Error(error); - }; - activeWS.onmessage = ({data}) => { - // Moving to top gives errors due to NativeModules not being initialized - const HMRLoadingView = require('HMRLoadingView'); - - data = JSON.parse(data); - - switch (data.type) { - case 'update-start': { - HMRLoadingView.showMessage('Hot Loading...'); - break; - } - case 'update': { - const { - modules, - sourceMappingURLs, - sourceURLs, - } = data.body; - - if (Platform.OS === 'ios') { - const RCTRedBox = require('NativeModules').RedBox; - RCTRedBox && RCTRedBox.dismiss && RCTRedBox.dismiss(); - } else { - const RCTExceptionsManager = require('NativeModules').ExceptionsManager; - RCTExceptionsManager && RCTExceptionsManager.dismissRedbox && RCTExceptionsManager.dismissRedbox(); - } - - modules.forEach(({id, code}, i) => { - code = code + '\n\n' + sourceMappingURLs[i]; - - // on JSC we need to inject from native for sourcemaps to work - // (Safari doesn't support `sourceMappingURL` nor any variant when - // evaluating code) but on Chrome we can simply use eval - const injectFunction = typeof global.nativeInjectHMRUpdate === 'function' - ? global.nativeInjectHMRUpdate - : eval; // eslint-disable-line no-eval - - injectFunction(code, sourceURLs[i]); - }); - - HMRLoadingView.hide(); - break; - } - case 'update-done': { - HMRLoadingView.hide(); - break; - } - case 'error': { - HMRLoadingView.hide(); - throw new Error(`${data.body.type}: ${data.body.message}`); - } - default: { - throw new Error(`Unexpected message: ${data}`); - } + }); + + hmrClient.on('update-start', () => { + HMRLoadingView.showMessage('Hot Loading...'); + }); + + hmrClient.on('update', () => { + if (Platform.OS === 'ios') { + const RCTRedBox = require('NativeModules').RedBox; + RCTRedBox && RCTRedBox.dismiss && RCTRedBox.dismiss(); + } else { + const RCTExceptionsManager = require('NativeModules').ExceptionsManager; + RCTExceptionsManager && + RCTExceptionsManager.dismissRedbox && + RCTExceptionsManager.dismissRedbox(); } - }; + }); + + hmrClient.on('update-done', () => { + HMRLoadingView.hide(); + }); + + hmrClient.on('error', data => { + HMRLoadingView.hide(); + throw new Error(`${data.type} ${data.message}`); + }); + + hmrClient.enable(); }, };