Skip to content

Commit ad879e5

Browse files
cpojerfacebook-github-bot
authored andcommitted
Add RCTDevSplitBundleLoader native module
Reviewed By: ejanzer Differential Revision: D21302418 fbshipit-source-id: a868f6dad3306190c7add26e8f9a976866c16aef
1 parent fc2b538 commit ad879e5

17 files changed

+343
-12
lines changed

Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec-generated.mm

+20
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,26 @@ + (RCTManagedPointer *)JS_NativeCameraRollManager_PhotoIdentifiersPage:(id)json
981981

982982

983983

984+
}
985+
986+
} // namespace react
987+
} // namespace facebook
988+
namespace facebook {
989+
namespace react {
990+
991+
992+
static facebook::jsi::Value __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
993+
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, PromiseKind, "loadBundle", @selector(loadBundle:resolve:reject:), args, count);
994+
}
995+
996+
997+
NativeDevSplitBundleLoaderSpecJSI::NativeDevSplitBundleLoaderSpecJSI(const ObjCTurboModule::InitParams &params)
998+
: ObjCTurboModule(params) {
999+
1000+
methodMap_["loadBundle"] = MethodMetadata {1, __hostFunction_NativeDevSplitBundleLoaderSpecJSI_loadBundle};
1001+
1002+
1003+
9841004
}
9851005

9861006
} // namespace react

Libraries/FBReactNativeSpec/FBReactNativeSpec/FBReactNativeSpec.h

+20
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,26 @@ namespace facebook {
918918
};
919919
} // namespace react
920920
} // namespace facebook
921+
@protocol NativeDevSplitBundleLoaderSpec <RCTBridgeModule, RCTTurboModule>
922+
923+
- (void)loadBundle:(NSString *)bundlePath
924+
resolve:(RCTPromiseResolveBlock)resolve
925+
reject:(RCTPromiseRejectBlock)reject;
926+
927+
@end
928+
namespace facebook {
929+
namespace react {
930+
/**
931+
* ObjC++ class for module 'DevSplitBundleLoader'
932+
*/
933+
934+
class JSI_EXPORT NativeDevSplitBundleLoaderSpecJSI : public ObjCTurboModule {
935+
public:
936+
NativeDevSplitBundleLoaderSpecJSI(const ObjCTurboModule::InitParams &params);
937+
938+
};
939+
} // namespace react
940+
} // namespace facebook
921941
@protocol NativeDeviceEventManagerSpec <RCTBridgeModule, RCTTurboModule>
922942

923943
- (void)invokeDefaultBackPressHandler;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
import type {TurboModule} from '../TurboModule/RCTExport';
14+
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
15+
16+
export interface Spec extends TurboModule {
17+
+loadBundle: (bundlePath: string) => Promise<void>;
18+
}
19+
20+
export default (TurboModuleRegistry.get<Spec>('DevSplitBundleLoader'): ?Spec);

React/Base/RCTBridge.h

+19-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ RCT_EXTERN NSString *const RCTJavaScriptWillStartExecutingNotification;
3333
*/
3434
RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification;
3535

36+
/**
37+
* This notification fires every time the bridge has finished loading an additional JS bundle.
38+
*/
39+
RCT_EXTERN NSString *const RCTAdditionalJavaScriptDidLoadNotification;
40+
3641
/**
3742
* This notification fires when the bridge failed to load the JS bundle. The
3843
* `error` key can be used to determine the error that occurred.
@@ -135,6 +140,12 @@ RCT_EXTERN NSString *const RCTBridgeDidDownloadScriptNotificationBridgeDescripti
135140
*/
136141
typedef NSArray<id<RCTBridgeModule>> * (^RCTBridgeModuleListProvider)(void);
137142

143+
/**
144+
* These blocks are used to report whether an additional bundle
145+
* fails or succeeds loading.
146+
*/
147+
typedef void (^RCTLoadAndExecuteErrorBlock)(NSError *error);
148+
138149
/**
139150
* This function returns the module name for a given class.
140151
*/
@@ -290,8 +301,15 @@ RCT_EXTERN void RCTEnableTurboModule(BOOL enabled);
290301
- (void)requestReload __deprecated_msg("Use RCTReloadCommand instead");
291302

292303
/**
293-
* Says whether bridge has started receiving calls from javascript.
304+
* Says whether bridge has started receiving calls from JavaScript.
294305
*/
295306
- (BOOL)isBatchActive;
296307

308+
/**
309+
* Loads and executes additional bundles in the VM for development.
310+
*/
311+
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
312+
onError:(RCTLoadAndExecuteErrorBlock)onError
313+
onComplete:(dispatch_block_t)onComplete;
314+
297315
@end

React/Base/RCTBridge.m

+12-3
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
#if RCT_ENABLE_INSPECTOR
1616
#import "RCTInspectorDevServerHelper.h"
1717
#endif
18+
#import "RCTDevLoadingViewProtocol.h"
1819
#import "RCTLog.h"
1920
#import "RCTModuleData.h"
2021
#import "RCTPerformanceLogger.h"
2122
#import "RCTProfile.h"
2223
#import "RCTReloadCommand.h"
2324
#import "RCTUtils.h"
2425

25-
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
26-
NSString *const RCTJavaScriptWillStartExecutingNotification = @"RCTJavaScriptWillStartExecutingNotification";
27-
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
26+
NSString *const RCTAdditionalJavaScriptDidLoadNotification = @"RCTAdditionalJavaScriptDidLoadNotification";
2827
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
28+
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
29+
NSString *const RCTJavaScriptWillStartExecutingNotification = @"RCTJavaScriptWillStartExecutingNotification";
30+
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
2931
NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification";
3032
NSString *const RCTDidSetupModuleNotification = @"RCTDidSetupModuleNotification";
3133
NSString *const RCTDidSetupModuleNotificationModuleNameKey = @"moduleName";
@@ -388,4 +390,11 @@ - (void)registerSegmentWithId:(NSUInteger)segmentId path:(NSString *)path
388390
[self.batchedBridge registerSegmentWithId:segmentId path:path];
389391
}
390392

393+
- (void)loadAndExecuteSplitBundleURL:(NSURL *)bundleURL
394+
onError:(RCTLoadAndExecuteErrorBlock)onError
395+
onComplete:(dispatch_block_t)onComplete
396+
{
397+
[self.batchedBridge loadAndExecuteSplitBundleURL:bundleURL onError:onError onComplete:onComplete];
398+
}
399+
391400
@end

React/Base/RCTJavaScriptLoader.mm

+17-3
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,23 @@ static void attemptAsynchronousLoadOfBundleAtURL(
301301
NSString *contentType = headers[@"Content-Type"];
302302
NSString *mimeType = [[contentType componentsSeparatedByString:@";"] firstObject];
303303
if (![mimeType isEqualToString:@"application/javascript"] && ![mimeType isEqualToString:@"text/javascript"]) {
304-
NSString *description = [NSString
305-
stringWithFormat:@"Expected MIME-Type to be 'application/javascript' or 'text/javascript', but got '%@'.",
306-
mimeType];
304+
NSString *description;
305+
if ([mimeType isEqualToString:@"application/json"]) {
306+
NSError *parseError;
307+
NSDictionary *jsonError = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
308+
if (!parseError && [jsonError isKindOfClass:[NSDictionary class]] &&
309+
[[jsonError objectForKey:@"message"] isKindOfClass:[NSString class]] &&
310+
[[jsonError objectForKey:@"message"] length]) {
311+
description = [jsonError objectForKey:@"message"];
312+
} else {
313+
description = [NSString stringWithFormat:@"Unknown error fetching '%@'.", scriptURL.absoluteString];
314+
}
315+
} else {
316+
description = [NSString
317+
stringWithFormat:
318+
@"Expected MIME-Type to be 'application/javascript' or 'text/javascript', but got '%@'.", mimeType];
319+
}
320+
307321
error = [NSError
308322
errorWithDomain:@"JSServer"
309323
code:NSURLErrorCannotParseResponse

React/CoreModules/BUCK

+3
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ rn_apple_library(
116116
) + react_module_plugin_providers(
117117
name = "DevLoadingView",
118118
native_class_func = "RCTDevLoadingViewCls",
119+
) + react_module_plugin_providers(
120+
name = "DevSplitBundleLoader",
121+
native_class_func = "RCTDevSplitBundleLoaderCls",
119122
),
120123
plugins_header = "FBCoreModulesPlugins.h",
121124
preprocessor_flags = OBJC_ARC_PREPROCESSOR_FLAGS + get_preprocessor_flags_for_build_mode() + rn_extra_build_flags() + [

React/CoreModules/CoreModulesPlugins.h

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Class RCTTVNavigationEventEmitterCls(void) __attribute__((used));
5353
Class RCTWebSocketExecutorCls(void) __attribute__((used));
5454
Class RCTWebSocketModuleCls(void) __attribute__((used));
5555
Class RCTDevLoadingViewCls(void) __attribute__((used));
56+
Class RCTDevSplitBundleLoaderCls(void) __attribute__((used));
5657

5758
#ifdef __cplusplus
5859
}

React/CoreModules/CoreModulesPlugins.mm

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Class RCTCoreModulesClassProvider(const char *name) {
4242
{"WebSocketExecutor", RCTWebSocketExecutorCls},
4343
{"WebSocketModule", RCTWebSocketModuleCls},
4444
{"DevLoadingView", RCTDevLoadingViewCls},
45+
{"DevSplitBundleLoader", RCTDevSplitBundleLoaderCls},
4546
};
4647

4748
auto p = sCoreModuleClassMap.find(name);

React/CoreModules/RCTDevSettings.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,14 @@
8282
- (void)toggleElementInspector;
8383

8484
/**
85-
* If loading bundle from metro, sets up HMRClient.
85+
* Set up the HMRClient if loading the bundle from Metro.
8686
*/
87-
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL;
87+
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL;
88+
89+
/**
90+
* Register additional bundles with the HMRClient.
91+
*/
92+
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL;
8893

8994
#if RCT_DEV_MENU
9095
- (void)addHandler:(id<RCTPackagerClientMethod>)handler

React/CoreModules/RCTDevSettings.mm

+19-2
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ - (void)addHandler:(id<RCTPackagerClientMethod>)handler forPackagerMethod:(NSStr
403403
#endif
404404
}
405405

406-
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
406+
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL
407407
{
408408
if (bundleURL && !bundleURL.fileURL) { // isHotLoadingAvailable check
409409
NSString *const path = [bundleURL.path substringFromIndex:1]; // Strip initial slash.
@@ -420,6 +420,20 @@ - (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
420420
}
421421
}
422422

423+
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL
424+
{
425+
if (bundleURL && !bundleURL.fileURL) { // isHotLoadingAvailable check
426+
if (self.bridge) {
427+
[self.bridge enqueueJSCall:@"HMRClient"
428+
method:@"registerBundle"
429+
args:@[ [bundleURL absoluteString] ]
430+
completion:NULL];
431+
} else {
432+
self.invokeJS(@"HMRClient", @"registerBundle", @[ [bundleURL absoluteString] ]);
433+
}
434+
}
435+
}
436+
423437
#pragma mark - Internal
424438

425439
/**
@@ -509,7 +523,10 @@ - (void)setProfilingEnabled:(BOOL)isProfilingEnabled
509523
- (void)toggleElementInspector
510524
{
511525
}
512-
- (void)setupHotModuleReloadClientIfApplicableForURL:(NSURL *)bundleURL
526+
- (void)setupHMRClientWithBundleURL:(NSURL *)bundleURL
527+
{
528+
}
529+
- (void)setupHMRClientWithAdditionalBundleURL:(NSURL *)bundleURL
513530
{
514531
}
515532
- (void)addMenuItem:(NSString *)title
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <React/RCTBridgeModule.h>
9+
#import <UIKit/UIKit.h>
10+
11+
@interface RCTDevSplitBundleLoader : NSObject <RCTBridgeModule>
12+
@end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <React/RCTDevSplitBundleLoader.h>
9+
10+
#import <FBReactNativeSpec/FBReactNativeSpec.h>
11+
#import <React/RCTBridge.h>
12+
#import <React/RCTBundleURLProvider.h>
13+
#import <React/RCTConvert.h>
14+
#import <React/RCTDefines.h>
15+
#import <React/RCTUtils.h>
16+
17+
#import "CoreModulesPlugins.h"
18+
19+
using namespace facebook::react;
20+
21+
@interface RCTDevSplitBundleLoader () <NativeDevSplitBundleLoaderSpec>
22+
@end
23+
24+
#if RCT_DEV_MENU
25+
26+
@implementation RCTDevSplitBundleLoader {
27+
}
28+
29+
@synthesize bridge = _bridge;
30+
31+
RCT_EXPORT_MODULE()
32+
33+
+ (BOOL)requiresMainQueueSetup
34+
{
35+
return NO;
36+
}
37+
38+
- (void)setBridge:(RCTBridge *)bridge
39+
{
40+
_bridge = bridge;
41+
}
42+
43+
RCT_EXPORT_METHOD(loadBundle
44+
: (NSString *)bundlePath resolve
45+
: (RCTPromiseResolveBlock)resolve reject
46+
: (RCTPromiseRejectBlock)reject)
47+
{
48+
NSURL *sourceURL = [[RCTBundleURLProvider sharedSettings] jsBundleURLForSplitBundleRoot:bundlePath];
49+
[_bridge loadAndExecuteSplitBundleURL:sourceURL
50+
onError:^(NSError *error) {
51+
reject(@"E_BUNDLE_LOAD_ERROR", [error localizedDescription], error);
52+
}
53+
onComplete:^() {
54+
resolve(@YES);
55+
}];
56+
}
57+
58+
- (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
59+
{
60+
return std::make_shared<NativeDevSplitBundleLoaderSpecJSI>(params);
61+
}
62+
63+
@end
64+
65+
#else
66+
67+
@implementation RCTDevSplitBundleLoader
68+
69+
+ (NSString *)moduleName
70+
{
71+
return nil;
72+
}
73+
- (void)loadBundle:(NSString *)bundlePath resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
74+
{
75+
}
76+
- (std::shared_ptr<TurboModule>)getTurboModule:(const ObjCTurboModule::InitParams &)params
77+
{
78+
return std::make_shared<NativeDevSplitBundleLoaderSpecJSI>(params);
79+
}
80+
81+
@end
82+
83+
#endif
84+
85+
Class RCTDevSplitBundleLoaderCls(void)
86+
{
87+
return RCTDevSplitBundleLoader.class;
88+
}

0 commit comments

Comments
 (0)