Add `RCTDevSplitBundleLoader` native module
Reviewed By: ejanzer Differential Revision: D21302418 fbshipit-source-id: a868f6dad3306190c7add26e8f9a976866c16aef
This commit is contained in:
Родитель
fc2b538f0d
Коммит
ad879e50bc
|
@ -981,6 +981,26 @@ namespace facebook {
|
|||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "loadBundle", @selector(loadBundle:resolve:reject:), args, count);
|
||||
}
|
||||
|
||||
|
||||
NativeDevSplitBundleLoaderSpecJSI::NativeDevSplitBundleLoaderSpecJSI(const ObjCTurboModule::InitParams ¶ms)
|
||||
: ObjCTurboModule(params) {
|
||||
|
||||
methodMap_["loadBundle"] = MethodMetadata {1, __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -918,6 +918,26 @@ namespace facebook {
|
|||
};
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@protocol NativeDevSplitBundleLoaderSpec <RCTBridgeModule, RCTTurboModule>
|
||||
|
||||
- (void)loadBundle:(NSString *)bundlePath
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject;
|
||||
|
||||
@end
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
/**
|
||||
* ObjC++ class for module 'DevSplitBundleLoader'
|
||||
*/
|
||||
|
||||
class JSI_EXPORT NativeDevSplitBundleLoaderSpecJSI : public ObjCTurboModule {
|
||||
public:
|
||||
NativeDevSplitBundleLoaderSpecJSI(const ObjCTurboModule::InitParams ¶ms);
|
||||
|
||||
};
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@protocol NativeDeviceEventManagerSpec <RCTBridgeModule, RCTTurboModule>
|
||||
|
||||
- (void)invokeDefaultBackPressHandler;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {TurboModule} from '../TurboModule/RCTExport';
|
||||
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
+loadBundle: (bundlePath: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.get<Spec>('DevSplitBundleLoader'): ?Spec);
|
|
@ -33,6 +33,11 @@ RCT_EXTERN NSString *const RCTJavaScriptWillStartExecutingNotification;
|
|||
*/
|
||||
RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification;
|
||||
|
||||
/**
|
||||
* This notification fires every time the bridge has finished loading an additional JS bundle.
|
||||
*/
|
||||
RCT_EXTERN NSString *const RCTAdditionalJavaScriptDidLoadNotification;
|
||||
|
||||
/**
|
||||
* This notification fires when the bridge failed to load the JS bundle. The
|
||||
* `error` key can be used to determine the error that occurred.
|
||||
|
@ -135,6 +140,12 @@ RCT_EXTERN NSString *const RCTBridgeDidDownloadScriptNotificationBridgeDescripti
|
|||
*/
|
||||
typedef NSArray<id<RCTBridgeModule>> * (^RCTBridgeModuleListProvider)(void);
|
||||
|
||||
/**
|
||||
* These blocks are used to report whether an additional bundle
|
||||
* fails or succeeds loading.
|
||||
*/
|
||||
typedef void (^RCTLoadAndExecuteErrorBlock)(NSError *error);
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
|
@ -290,8 +301,15 @@ RCT_EXTERN void RCTEnableTurboModule(BOOL enabled);
|
|||
- (void)requestReload __deprecated_msg("Use RCTReloadCommand instead");
|
||||
|
||||
/**
|
||||
* Says whether bridge has started receiving calls from javascript.
|
||||
* Says whether bridge has started receiving calls from JavaScript.
|
||||
*/
|
||||
- (BOOL)isBatchActive;
|
||||
|
||||
/**
|
||||
* Loads and executes additional bundles in the VM for development.
|
||||
*/
|
||||
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
|
||||
onError:(RCTLoadAndExecuteErrorBlock)onError
|
||||
onComplete:(dispatch_block_t)onComplete;
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#if RCT_ENABLE_INSPECTOR
|
||||
#import "RCTInspectorDevServerHelper.h"
|
||||
#endif
|
||||
#import "RCTDevLoadingViewProtocol.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTModuleData.h"
|
||||
#import "RCTPerformanceLogger.h"
|
||||
|
@ -22,10 +23,11 @@
|
|||
#import "RCTReloadCommand.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
|
||||
NSString *const RCTJavaScriptWillStartExecutingNotification = @"RCTJavaScriptWillStartExecutingNotification";
|
||||
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
||||
NSString *const RCTAdditionalJavaScriptDidLoadNotification = @"RCTAdditionalJavaScriptDidLoadNotification";
|
||||
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
||||
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
||||
NSString *const RCTJavaScriptWillStartExecutingNotification = @"RCTJavaScriptWillStartExecutingNotification";
|
||||
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
|
||||
NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification";
|
||||
NSString *const RCTDidSetupModuleNotification = @"RCTDidSetupModuleNotification";
|
||||
NSString *const RCTDidSetupModuleNotificationModuleNameKey = @"moduleName";
|
||||
|
@ -388,4 +390,11 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
|
|||
[self.batchedBridge registerSegmentWithId:segmentId path:path];
|
||||
}
|
||||
|
||||
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
|
||||
onError:(RCTLoadAndExecuteErrorBlock)onError
|
||||
onComplete:(dispatch_block_t)onComplete
|
||||
{
|
||||
[self.batchedBridge loadAndExecuteSplitBundleURL:bundleURL onError:onError onComplete:onComplete];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -301,9 +301,23 @@ static void attemptAsynchronousLoadOfBundleAtURL(
|
|||
NSString *contentType = headers[@"Content-Type"];
|
||||
NSString *mimeType = [[contentType componentsSeparatedByString:@";"] firstObject];
|
||||
if (![mimeType isEqualToString:@"application/javascript"] && ![mimeType isEqualToString:@"text/javascript"]) {
|
||||
NSString *description = [NSString
|
||||
stringWithFormat:@"Expected MIME-Type to be 'application/javascript' or 'text/javascript', but got '%@'.",
|
||||
mimeType];
|
||||
NSString *description;
|
||||
if ([mimeType isEqualToString:@"application/json"]) {
|
||||
NSError *parseError;
|
||||
NSDictionary *jsonError = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
|
||||
if (!parseError && [jsonError isKindOfClass:[NSDictionary class]] &&
|
||||
[[jsonError objectForKey:@"message"] isKindOfClass:[NSString class]] &&
|
||||
[[jsonError objectForKey:@"message"] length]) {
|
||||
description = [jsonError objectForKey:@"message"];
|
||||
} else {
|
||||
description = [NSString stringWithFormat:@"Unknown error fetching '%@'.", scriptURL.absoluteString];
|
||||
}
|
||||
} else {
|
||||
description = [NSString
|
||||
stringWithFormat:
|
||||
@"Expected MIME-Type to be 'application/javascript' or 'text/javascript', but got '%@'.", mimeType];
|
||||
}
|
||||
|
||||
error = [NSError
|
||||
errorWithDomain:@"JSServer"
|
||||
code:NSURLErrorCannotParseResponse
|
||||
|
|
|
@ -116,6 +116,9 @@ rn_apple_library(
|
|||
) + react_module_plugin_providers(
|
||||
name = "DevLoadingView",
|
||||
native_class_func = "RCTDevLoadingViewCls",
|
||||
) + react_module_plugin_providers(
|
||||
name = "DevSplitBundleLoader",
|
||||
native_class_func = "RCTDevSplitBundleLoaderCls",
|
||||
),
|
||||
plugins_header = "FBCoreModulesPlugins.h",
|
||||
preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode() + rn_extra_build_flags() + [
|
||||
|
|
|
@ -53,6 +53,7 @@ Class RCTTVNavigationEventEmitterCls(void) __attribute__((used));
|
|||
Class RCTWebSocketExecutorCls(void) __attribute__((used));
|
||||
Class RCTWebSocketModuleCls(void) __attribute__((used));
|
||||
Class RCTDevLoadingViewCls(void) __attribute__((used));
|
||||
Class RCTDevSplitBundleLoaderCls(void) __attribute__((used));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ Class RCTCoreModulesClassProvider(const char *name) {
|
|||
{"WebSocketExecutor", RCTWebSocketExecutorCls},
|
||||
{"WebSocketModule", RCTWebSocketModuleCls},
|
||||
{"DevLoadingView", RCTDevLoadingViewCls},
|
||||
{"DevSplitBundleLoader", RCTDevSplitBundleLoaderCls},
|
||||
};
|
||||
|
||||
auto p = sCoreModuleClassMap.find(name);
|
||||
|
|
|
@ -82,9 +82,14 @@
|
|||
- (void)toggleElementInspector;
|
||||
|
||||
/**
|
||||
* If loading bundle from metro, sets up HMRClient.
|
||||
* Set up the HMRClient if loading the bundle from Metro.
|
||||
*/
|
||||
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL;
|
||||
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL;
|
||||
|
||||
/**
|
||||
* Register additional bundles with the HMRClient.
|
||||
*/
|
||||
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL;
|
||||
|
||||
#if RCT_DEV_MENU
|
||||
- (void)addHandler:(id<RCTPackagerClientMethod>)handler
|
||||
|
|
|
@ -403,7 +403,7 @@ RCT_EXPORT_METHOD(addMenuItem : (NSString *)title)
|
|||
#endif
|
||||
}
|
||||
|
||||
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
|
||||
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
if (bundleURL && !bundleURL.fileURL) { // isHotLoadingAvailable check
|
||||
NSString *const path = [bundleURL.path substringFromIndex:1]; // Strip initial slash.
|
||||
|
@ -420,6 +420,20 @@ RCT_EXPORT_METHOD(addMenuItem : (NSString *)title)
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
if (bundleURL && !bundleURL.fileURL) { // isHotLoadingAvailable check
|
||||
if (self.bridge) {
|
||||
[self.bridge enqueueJSCall:@"HMRClient"
|
||||
method:@"registerBundle"
|
||||
args:@[ [bundleURL absoluteString] ]
|
||||
completion:NULL];
|
||||
} else {
|
||||
self.invokeJS(@"HMRClient", @"registerBundle", @[ [bundleURL absoluteString] ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Internal
|
||||
|
||||
/**
|
||||
|
@ -509,7 +523,10 @@ RCT_EXPORT_METHOD(addMenuItem : (NSString *)title)
|
|||
- (void)toggleElementInspector
|
||||
{
|
||||
}
|
||||
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
|
||||
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
}
|
||||
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL
|
||||
{
|
||||
}
|
||||
- (void)addMenuItem:(NSString *)title
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RCTDevSplitBundleLoader : NSObject <RCTBridgeModule>
|
||||
@end
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTDevSplitBundleLoader.h>
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTDefines.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "CoreModulesPlugins.h"
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
@interface RCTDevSplitBundleLoader () <NativeDevSplitBundleLoaderSpec>
|
||||
@end
|
||||
|
||||
#if RCT_DEV_MENU
|
||||
|
||||
@implementation RCTDevSplitBundleLoader {
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(loadBundle
|
||||
: (NSString *)bundlePath resolve
|
||||
: (RCTPromiseResolveBlock)resolve reject
|
||||
: (RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
NSURL *sourceURL = [[RCTBundleURLProvider sharedSettings] jsBundleURLForSplitBundleRoot:bundlePath];
|
||||
[_bridge loadAndExecuteSplitBundleURL:sourceURL
|
||||
onError:^(NSError *error) {
|
||||
reject(@"E_BUNDLE_LOAD_ERROR", [error localizedDescription], error);
|
||||
}
|
||||
onComplete:^() {
|
||||
resolve(@YES);
|
||||
}];
|
||||
}
|
||||
|
||||
- (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return std::make_shared<NativeDevSplitBundleLoaderSpecJSI>(params);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
@implementation RCTDevSplitBundleLoader
|
||||
|
||||
+ (NSString *)moduleName
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
- (void)loadBundle:(NSString *)bundlePath resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
|
||||
{
|
||||
}
|
||||
- (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
|
||||
{
|
||||
return std::make_shared<NativeDevSplitBundleLoaderSpecJSI>(params);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
Class RCTDevSplitBundleLoaderCls(void)
|
||||
{
|
||||
return RCTDevSplitBundleLoader.class;
|
||||
}
|
|
@ -966,9 +966,49 @@ struct RCTInstanceCallback : public InstanceCallback {
|
|||
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
|
||||
}
|
||||
|
||||
[self.devSettings setupHotModuleReloadClientIfApplicableForURL:self.bundleURL];
|
||||
[self.devSettings setupHMRClientWithBundleURL:self.bundleURL];
|
||||
}
|
||||
|
||||
#if RCT_DEV_MENU
|
||||
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
|
||||
onError:(RCTLoadAndExecuteErrorBlock)onError
|
||||
onComplete:(dispatch_block_t)onComplete
|
||||
{
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
[RCTJavaScriptLoader loadBundleAtURL:bundleURL
|
||||
onProgress:^(RCTLoadingProgress *progressData) {
|
||||
#if (RCT_DEV_MENU | RCT_ENABLE_LOADING_VIEW) && __has_include(<React/RCTDevLoadingViewProtocol.h>)
|
||||
id<RCTDevLoadingViewProtocol> loadingView = [weakSelf moduleForName:@"DevLoadingView"
|
||||
lazilyLoadIfNecessary:YES];
|
||||
[loadingView updateProgress:progressData];
|
||||
#endif
|
||||
}
|
||||
onComplete:^(NSError *error, RCTSource *source) {
|
||||
if (error) {
|
||||
onError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
[self enqueueApplicationScript:source.data
|
||||
url:source.url
|
||||
onComplete:^{
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName:RCTAdditionalJavaScriptDidLoadNotification
|
||||
object:self->_parentBridge
|
||||
userInfo:@{@"bridge" : self}];
|
||||
[self.devSettings setupHMRClientWithAdditionalBundleURL:source.url];
|
||||
onComplete();
|
||||
}];
|
||||
}];
|
||||
}
|
||||
#else
|
||||
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
|
||||
onError:(RCTLoadAndExecuteErrorBlock)onError
|
||||
onComplete:(dispatch_block_t)onComplete
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)handleError:(NSError *)error
|
||||
{
|
||||
// This is generally called when the infrastructure throws an
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*
|
||||
* <p>Generated by an internal genrule from Flow types.
|
||||
*
|
||||
* @generated
|
||||
* @nolint
|
||||
*/
|
||||
|
||||
package com.facebook.fbreact.specs;
|
||||
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactModuleWithSpec;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
|
||||
public abstract class NativeDevSplitBundleLoaderSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule {
|
||||
public NativeDevSplitBundleLoaderSpec(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public abstract void loadBundle(String bundlePath, Promise promise);
|
||||
}
|
|
@ -819,6 +819,26 @@ namespace facebook {
|
|||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<JavaTurboModule&>(turboModule).invokeJavaMethod(rt, PromiseKind, "loadBundle", "(Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count);
|
||||
}
|
||||
|
||||
|
||||
NativeDevSplitBundleLoaderSpecJSI::NativeDevSplitBundleLoaderSpecJSI(const JavaTurboModule::InitParams ¶ms)
|
||||
: JavaTurboModule(params) {
|
||||
|
||||
methodMap_["loadBundle"] = MethodMetadata {1, __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
|
|
@ -255,6 +255,20 @@ namespace facebook {
|
|||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
/**
|
||||
* C++ class for module 'DevSplitBundleLoader'
|
||||
*/
|
||||
|
||||
class JSI_EXPORT NativeDevSplitBundleLoaderSpecJSI : public JavaTurboModule {
|
||||
public:
|
||||
NativeDevSplitBundleLoaderSpecJSI(const JavaTurboModule::InitParams ¶ms);
|
||||
|
||||
};
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче