Enable HMR
Reviewed By: svcscm Differential Revision: D2932137 fb-gh-sync-id: 8bfab09aaac22ae498ac4fa896eee495111abc0d shipit-source-id: 8bfab09aaac22ae498ac4fa896eee495111abc0d
This commit is contained in:
parent
8eddead868
commit
e018aa3100
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
const Platform = require('Platform');
|
||||
const invariant = require('invariant');
|
||||
const processColor = require('processColor');
|
||||
|
||||
|
@ -18,23 +19,26 @@ const processColor = require('processColor');
|
|||
* runtime to reflects those changes.
|
||||
*/
|
||||
const HMRClient = {
|
||||
enable(platform, bundleEntry) {
|
||||
enable(platform, bundleEntry, host, port) {
|
||||
invariant(platform, 'Missing required parameter `platform`');
|
||||
invariant(bundleEntry, 'Missing required paramenter `bundleEntry`');
|
||||
|
||||
// TODO(martinb) receive host and port as parameters
|
||||
const host = 'localhost';
|
||||
const port = '8081';
|
||||
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');
|
||||
|
||||
const activeWS = new WebSocket(
|
||||
`ws://${host}:${port}/hot?platform=${platform}&` +
|
||||
`bundleEntry=${bundleEntry.replace('.bundle', '.js')}`
|
||||
);
|
||||
const wsHostPort = port !== null && port !== ''
|
||||
? `${host}:${port}`
|
||||
: host;
|
||||
|
||||
// Build the websocket url
|
||||
const wsUrl = `ws://${wsHostPort}/hot?` +
|
||||
`platform=${platform}&` +
|
||||
`bundleEntry=${bundleEntry.replace('.bundle', '.js')}`;
|
||||
|
||||
const activeWS = new WebSocket(wsUrl);
|
||||
activeWS.onerror = (e) => {
|
||||
throw new Error(
|
||||
`Hot loading isn't working because it cannot connect to the development server.
|
||||
|
@ -50,10 +54,16 @@ Error: ${e.message}`
|
|||
);
|
||||
};
|
||||
activeWS.onmessage = ({data}) => {
|
||||
const DevLoadingView = require('NativeModules').DevLoadingView;
|
||||
let DevLoadingView = require('NativeModules').DevLoadingView;
|
||||
if (!DevLoadingView) {
|
||||
DevLoadingView = {
|
||||
showMessage() {},
|
||||
hide() {},
|
||||
};
|
||||
}
|
||||
data = JSON.parse(data);
|
||||
|
||||
switch(data.type) {
|
||||
switch (data.type) {
|
||||
case 'update-start': {
|
||||
DevLoadingView.showMessage(
|
||||
'Hot Loading...',
|
||||
|
@ -67,8 +77,13 @@ Error: ${e.message}`
|
|||
const sourceMappingURLs = data.body.sourceMappingURLs;
|
||||
const sourceURLs = data.body.sourceURLs;
|
||||
|
||||
const RCTRedBox = require('NativeModules').RedBox;
|
||||
RCTRedBox && RCTRedBox.dismiss && RCTRedBox.dismiss();
|
||||
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((code, i) => {
|
||||
code = code + '\n\n' + sourceMappingURLs[i];
|
||||
|
@ -82,8 +97,8 @@ Error: ${e.message}`
|
|||
// 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 __injectHMRUpdate === 'function'
|
||||
? __injectHMRUpdate
|
||||
const injectFunction = typeof global.nativeInjectHMRUpdate === 'function'
|
||||
? global.nativeInjectHMRUpdate
|
||||
: eval;
|
||||
|
||||
injectFunction(code, sourceURLs[i]);
|
||||
|
|
|
@ -461,7 +461,9 @@ RCT_EXTERN NSArray<Class> *RCTGetModuleClasses(void);
|
|||
|
||||
if (RCTGetURLQueryParam(self.bundleURL, @"hot")) {
|
||||
NSString *path = [self.bundleURL.path substringFromIndex:1]; // strip initial slash
|
||||
[self enqueueJSCall:@"HMRClient.enable" args:@[@"ios", path]];
|
||||
NSString *host = self.bundleURL.host;
|
||||
NSNumber *port = self.bundleURL.port;
|
||||
[self enqueueJSCall:@"HMRClient.enable" args:@[@"ios", path, host, RCTNullIfNil(port)]];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -341,6 +341,20 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
|||
name:event
|
||||
object:nil];
|
||||
}
|
||||
|
||||
// Inject handler used by HMR
|
||||
[self addSynchronousHookWithName:@"nativeInjectHMRUpdate" usingBlock:^(NSString *sourceCode, NSString *sourceCodeURL) {
|
||||
RCTJSCExecutor *strongSelf = weakSelf;
|
||||
if (!strongSelf.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSStringRef execJSString = JSStringCreateWithUTF8CString(sourceCode.UTF8String);
|
||||
JSStringRef jsURL = JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String);
|
||||
JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, NULL);
|
||||
JSStringRelease(jsURL);
|
||||
JSStringRelease(execJSString);
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -513,21 +527,6 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context)
|
|||
RCTAssertParam(sourceURL);
|
||||
|
||||
__weak RCTJSCExecutor *weakSelf = self;
|
||||
#if RCT_DEV
|
||||
_context.context[@"__injectHMRUpdate"] = ^(NSString *sourceCode, NSString *sourceCodeURL) {
|
||||
RCTJSCExecutor *strongSelf = weakSelf;
|
||||
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSStringRef execJSString = JSStringCreateWithUTF8CString(sourceCode.UTF8String);
|
||||
JSStringRef jsURL = JSStringCreateWithUTF8CString(sourceCodeURL.UTF8String);
|
||||
JSEvaluateScript(strongSelf->_context.ctx, execJSString, NULL, jsURL, 0, NULL);
|
||||
JSStringRelease(jsURL);
|
||||
JSStringRelease(execJSString);
|
||||
};
|
||||
#endif
|
||||
|
||||
[self executeBlockOnJavaScriptQueue:RCTProfileBlock((^{
|
||||
RCTJSCExecutor *strongSelf = weakSelf;
|
||||
|
|
|
@ -19,6 +19,7 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
|||
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.modules.core.ExceptionsManagerModule;
|
||||
import com.facebook.react.devsupport.HMRClient;
|
||||
import com.facebook.react.modules.core.JSTimersExecution;
|
||||
import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
|
||||
import com.facebook.react.modules.core.Timing;
|
||||
|
@ -95,6 +96,7 @@ import com.facebook.systrace.Systrace;
|
|||
RCTNativeAppEventEmitter.class,
|
||||
AppRegistry.class,
|
||||
com.facebook.react.bridge.Systrace.class,
|
||||
HMRClient.class,
|
||||
DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
|
|||
void addCustomDevOption(String optionName, DevOptionHandler optionHandler);
|
||||
void showNewJSError(String message, ReadableArray details, int errorCookie);
|
||||
void updateJSError(final String message, final ReadableArray details, final int errorCookie);
|
||||
void hideRedboxDialog();
|
||||
void showDevOptionsDialog();
|
||||
void setDevSupportEnabled(boolean isDevSupportEnabled);
|
||||
boolean getDevSupportEnabled();
|
||||
|
|
|
@ -13,6 +13,8 @@ import javax.annotation.Nullable;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
@ -40,7 +42,6 @@ import com.facebook.react.R;
|
|||
import com.facebook.react.bridge.CatalystInstance;
|
||||
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.JavaJSExecutor;
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
@ -216,6 +217,14 @@ public class DevSupportManagerImpl implements DevSupportManager {
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideRedboxDialog() {
|
||||
// dismiss redbox if exists
|
||||
if (mRedBoxDialog != null) {
|
||||
mRedBoxDialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void showNewError(
|
||||
final String message,
|
||||
final StackFrame[] stack,
|
||||
|
@ -522,6 +531,18 @@ public class DevSupportManagerImpl implements DevSupportManager {
|
|||
mDebugOverlayController = new DebugOverlayController(reactContext);
|
||||
}
|
||||
|
||||
if (mDevSettings.isHotModuleReplacementEnabled() && mCurrentContext != null) {
|
||||
try {
|
||||
URL sourceUrl = new URL(getSourceUrl());
|
||||
String path = sourceUrl.getPath().substring(1); // strip initial slash in path
|
||||
String host = sourceUrl.getHost();
|
||||
int port = sourceUrl.getPort();
|
||||
mCurrentContext.getJSModule(HMRClient.class).enable("android", path, host, port);
|
||||
} catch (MalformedURLException e) {
|
||||
showNewJavaError(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
reloadSettings();
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,11 @@ public class DisabledDevSupportManager implements DevSupportManager {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideRedboxDialog() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showDevOptionsDialog() {
|
||||
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
|
||||
/**
|
||||
* JS module interface for HMRClient
|
||||
*
|
||||
* The HMR(Hot Module Replacement)Client allows for the application to receive updates
|
||||
* from the packager server (over a web socket), allowing for injection of JavaScript to
|
||||
* the running application (without a refresh).
|
||||
*/
|
||||
public interface HMRClient extends JavaScriptModule {
|
||||
|
||||
/**
|
||||
* Enable the HMRClient so that the client will receive updates
|
||||
* from the packager server.
|
||||
* @param platform The platform in which HMR updates will be enabled. Should be "android".
|
||||
* @param bundleEntry The path to the bundle entry file (e.g. index.ios.bundle).
|
||||
* @param host The host that the HMRClient should communicate with.
|
||||
* @param port The port that the HMRClient should communicate with on the host.
|
||||
*/
|
||||
void enable(String platform, String bundleEntry, String host, int port);
|
||||
}
|
|
@ -77,4 +77,11 @@ public class ExceptionsManagerModule extends BaseJavaModule {
|
|||
mDevSupportManager.updateJSError(title, details, exceptionId);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void dismissRedbox() {
|
||||
if (mDevSupportManager.getDevSupportEnabled()) {
|
||||
mDevSupportManager.hideRedboxDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,13 @@ static JSValueRef nativePerformanceNow(
|
|||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
static JSValueRef nativeInjectHMRUpdate(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
|
||||
static std::string executeJSCallWithJSC(
|
||||
JSGlobalContextRef ctx,
|
||||
|
@ -93,6 +100,7 @@ JSCExecutor::JSCExecutor(FlushImmediateCallback cb, const std::string& cacheDir)
|
|||
installGlobalFunction(m_context, "nativeStartWorker", nativeStartWorker);
|
||||
installGlobalFunction(m_context, "nativePostMessageToWorker", nativePostMessageToWorker);
|
||||
installGlobalFunction(m_context, "nativeTerminateWorker", nativeTerminateWorker);
|
||||
installGlobalFunction(m_context, "nativeInjectHMRUpdate", nativeInjectHMRUpdate);
|
||||
|
||||
installGlobalFunction(m_context, "nativeLoggingHook", JSLogging::nativeHook);
|
||||
|
||||
|
@ -140,7 +148,7 @@ void JSCExecutor::executeApplicationScript(
|
|||
if (!jsSourceURL) {
|
||||
evaluateScript(m_context, jsScript, jsSourceURL);
|
||||
} else {
|
||||
// If we're evaluating a script, get the device's cache dir
|
||||
// If we're evaluating a script, get the device's cache dir
|
||||
// in which a cache file for that script will be stored.
|
||||
evaluateScript(m_context, jsScript, jsSourceURL, m_deviceCacheDir.c_str());
|
||||
}
|
||||
|
@ -471,4 +479,16 @@ static JSValueRef nativePerformanceNow(
|
|||
return JSValueMakeNumber(ctx, (nano / (double)NANOSECONDS_IN_MILLISECOND));
|
||||
}
|
||||
|
||||
static JSValueRef nativeInjectHMRUpdate(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception) {
|
||||
String execJSString = Value(ctx, arguments[0]).toString();
|
||||
String jsURL = Value(ctx, arguments[1]).toString();
|
||||
evaluateScript(ctx, execJSString, jsURL);
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
Loading…
Reference in New Issue