Skip to content

Commit

Permalink
Add RCTDevSplitBundleLoader native module
Browse files Browse the repository at this point in the history
Reviewed By: ejanzer

Differential Revision: D21302418

fbshipit-source-id: a868f6dad3306190c7add26e8f9a976866c16aef
  • Loading branch information
cpojer authored and facebook-github-bot committed Jun 8, 2020
1 parent fc2b538 commit ad879e5
Show file tree
Hide file tree
Showing 17 changed files with 343 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,26 @@ + (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifiersPage:(id)json



}

} // 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 &params)
: ObjCTurboModule(params) {

methodMap_["loadBundle"] = MethodMetadata {1, __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle};



}

} // namespace react
Expand Down
20 changes: 20 additions & 0 deletions Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 &params);

};
} // namespace react
} // namespace facebook
@protocol NativeDeviceEventManagerSpec <RCTBridgeModule, RCTTurboModule>

- (void)invokeDefaultBackPressHandler;
Expand Down
20 changes: 20 additions & 0 deletions Libraries/Utilities/NativeDevSplitBundleLoader.js
Original file line number Diff line number Diff line change
@@ -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);
20 changes: 19 additions & 1 deletion React/Base/RCTBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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 changes: 12 additions & 3 deletions React/Base/RCTBridge.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@
#if RCT_ENABLE_INSPECTOR
#import "RCTInspectorDevServerHelper.h"
#endif
#import "RCTDevLoadingViewProtocol.h"
#import "RCTLog.h"
#import "RCTModuleData.h"
#import "RCTPerformanceLogger.h"
#import "RCTProfile.h"
#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";
Expand Down Expand Up @@ -388,4 +390,11 @@ - (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path
[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
20 changes: 17 additions & 3 deletions React/Base/RCTJavaScriptLoader.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions React/CoreModules/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -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() + [
Expand Down
1 change: 1 addition & 0 deletions React/CoreModules/CoreModulesPlugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
1 change: 1 addition & 0 deletions React/CoreModules/CoreModulesPlugins.mm
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Class RCTCoreModulesClassProvider(const char *name) {
{"WebSocketExecutor", RCTWebSocketExecutorCls},
{"WebSocketModule", RCTWebSocketModuleCls},
{"DevLoadingView", RCTDevLoadingViewCls},
{"DevSplitBundleLoader", RCTDevSplitBundleLoaderCls},
};

auto p = sCoreModuleClassMap.find(name);
Expand Down
9 changes: 7 additions & 2 deletions React/CoreModules/RCTDevSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions React/CoreModules/RCTDevSettings.mm
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ - (void)addHandler:(id<RCTPackagerClientMethod>)handler forPackagerMethod:(NSStr
#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.
Expand All @@ -420,6 +420,20 @@ - (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
}
}

- (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

/**
Expand Down Expand Up @@ -509,7 +523,10 @@ - (void)setProfilingEnabled:(BOOL)isProfilingEnabled
- (void)toggleElementInspector
{
}
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL
{
}
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL
{
}
- (void)addMenuItem:(NSString *)title
Expand Down
12 changes: 12 additions & 0 deletions React/CoreModules/RCTDevSplitBundleLoader.h
Original file line number Diff line number Diff line change
@@ -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
88 changes: 88 additions & 0 deletions React/CoreModules/RCTDevSplitBundleLoader.mm
Original file line number Diff line number Diff line change
@@ -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;
}
Loading

0 comments on commit ad879e5

Please sign in to comment.