@onekeyfe/react-native-split-bundle-loader 3.0.89 → 3.0.91
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ios/SplitBundleLoader.h +7 -1
- package/ios/SplitBundleLoader.mm +147 -2
- package/package.json +1 -1
package/ios/SplitBundleLoader.h
CHANGED
|
@@ -24,5 +24,11 @@
|
|
|
24
24
|
/// @param bundlePath Absolute filesystem path to the bundle file.
|
|
25
25
|
/// @param host The RCTHost whose runtime should evaluate the bundle.
|
|
26
26
|
+ (void)loadEntryBundle:(NSString *)bundlePath inHost:(id)host;
|
|
27
|
-
|
|
27
|
+
/// Download and evaluate a dev-vendor modules-only entry bundle after the
|
|
28
|
+
/// host has booted from its local common HBC. HMR setup is queued after the
|
|
29
|
+
/// entry bundle evaluation.
|
|
30
|
+
+ (void)loadDevVendorEntryBundle:(NSURL *)bundleURL
|
|
31
|
+
hmrBundleURL:(NSURL *)hmrBundleURL
|
|
32
|
+
fingerprint:(NSString *)fingerprint
|
|
33
|
+
inHost:(id)host;
|
|
28
34
|
@end
|
package/ios/SplitBundleLoader.mm
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#import "SplitBundleLoader.h"
|
|
2
2
|
#import "SBLLogger.h"
|
|
3
|
+
#import <React/RCTAssert.h>
|
|
4
|
+
#import <React/RCTBridgeModule.h>
|
|
5
|
+
#import <React/RCTDevSettings.h>
|
|
6
|
+
#import <React/RCTJavaScriptLoader.h>
|
|
3
7
|
#import <ReactCommon/RCTHost.h>
|
|
4
8
|
#import <ReactCommon/RCTHost+Internal.h>
|
|
5
9
|
#import <ReactCommon/RCTInstance.h>
|
|
@@ -11,7 +15,6 @@
|
|
|
11
15
|
#include <cstdint>
|
|
12
16
|
|
|
13
17
|
namespace {
|
|
14
|
-
|
|
15
18
|
// Zero-copy jsi::Buffer over an NSData (M4/M5).
|
|
16
19
|
//
|
|
17
20
|
// WHY: the previous code did `std::string(data.bytes, data.length)` inside a
|
|
@@ -901,7 +904,149 @@ typedef NS_ENUM(NSInteger, ESegmentEvalError) {
|
|
|
901
904
|
double totalMs = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
|
|
902
905
|
[SBLLogger info:[NSString stringWithFormat:@"[SplitBundle] loadEntryBundle: %@ dispatched in %.1fms (eval is async)", sourceURL, totalMs]];
|
|
903
906
|
}
|
|
904
|
-
|
|
907
|
+
+ (void)loadDevVendorEntryBundle:(NSURL *)bundleURL
|
|
908
|
+
hmrBundleURL:(NSURL *)hmrBundleURL
|
|
909
|
+
fingerprint:(NSString *)fingerprint
|
|
910
|
+
inHost:(id)host
|
|
911
|
+
{
|
|
912
|
+
if (!host || !bundleURL || bundleURL.fileURL || bundleURL.host.length == 0 ||
|
|
913
|
+
!hmrBundleURL || hmrBundleURL.fileURL || hmrBundleURL.host.length == 0 ||
|
|
914
|
+
fingerprint.length == 0) {
|
|
915
|
+
[SBLLogger warn:@"loadDevVendorEntryBundle: invalid arguments"];
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
RCTHost *reactHost = (RCTHost *)host;
|
|
919
|
+
Ivar ivar = class_getInstanceVariable([reactHost class], "_instance");
|
|
920
|
+
RCTInstance *instance = ivar ? object_getIvar(reactHost, ivar) : nil;
|
|
921
|
+
if (!instance) {
|
|
922
|
+
[SBLLogger warn:@"loadDevVendorEntryBundle: RCTInstance is unavailable"];
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
NSURLComponents *components = [NSURLComponents componentsWithURL:hmrBundleURL
|
|
926
|
+
resolvingAgainstBaseURL:NO];
|
|
927
|
+
NSString *path = [components.path hasPrefix:@"/"]
|
|
928
|
+
? [components.path substringFromIndex:1]
|
|
929
|
+
: components.path;
|
|
930
|
+
NSString *scheme = components.scheme.lowercaseString;
|
|
931
|
+
NSString *hostName = components.host;
|
|
932
|
+
NSNumber *port = components.port;
|
|
933
|
+
if (!port && [scheme isEqualToString:@"http"]) {
|
|
934
|
+
port = @80;
|
|
935
|
+
} else if (!port && [scheme isEqualToString:@"https"]) {
|
|
936
|
+
port = @443;
|
|
937
|
+
}
|
|
938
|
+
RCTDevSettings *devSettings =
|
|
939
|
+
(RCTDevSettings *)[reactHost.moduleRegistry moduleForName:"DevSettings"];
|
|
940
|
+
if (!devSettings || path.length == 0 || hostName.length == 0 ||
|
|
941
|
+
scheme.length == 0 || port.integerValue <= 0 || port.integerValue > 65535) {
|
|
942
|
+
NSError *configurationError = [NSError errorWithDomain:@"SplitBundleLoader"
|
|
943
|
+
code:3
|
|
944
|
+
userInfo:@{
|
|
945
|
+
NSLocalizedDescriptionKey:
|
|
946
|
+
@"Invalid dev-vendor HMR configuration"
|
|
947
|
+
}];
|
|
948
|
+
[SBLLogger error:@"loadDevVendorEntryBundle: invalid HMR configuration"];
|
|
949
|
+
RCTFatal(configurationError);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
__weak RCTHost *weakHost = reactHost;
|
|
953
|
+
__weak RCTInstance *weakInstance = instance;
|
|
954
|
+
__block NSError *downloadError = nil;
|
|
955
|
+
__block RCTSource *downloadedSource = nil;
|
|
956
|
+
dispatch_semaphore_t downloadReady = dispatch_semaphore_create(0);
|
|
957
|
+
dispatch_semaphore_t hostRegistrationReady = dispatch_semaphore_create(0);
|
|
958
|
+
[RCTJavaScriptLoader loadBundleAtURL:bundleURL
|
|
959
|
+
onProgress:^(RCTLoadingProgress *progress) { (void)progress; }
|
|
960
|
+
onComplete:^(NSError *error, RCTSource *source) {
|
|
961
|
+
downloadError = error;
|
|
962
|
+
downloadedSource = source;
|
|
963
|
+
dispatch_semaphore_signal(downloadReady);
|
|
964
|
+
}];
|
|
965
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
966
|
+
// Match the production loader's next-run-loop deferral so Expo finishes
|
|
967
|
+
// native module registration before the entry begins evaluating.
|
|
968
|
+
dispatch_semaphore_signal(hostRegistrationReady);
|
|
969
|
+
});
|
|
970
|
+
// Queue this before RCTHost starts/restarts surfaces. The executor remains
|
|
971
|
+
// buffered until common.hbc completes, then waits only for Metro I/O. This
|
|
972
|
+
// preserves common -> delta -> runApplication ordering without blocking the
|
|
973
|
+
// main thread. Let RCTJavaScriptLoader completion govern the cold-build wait:
|
|
974
|
+
// a fixed deadline can crash a healthy but contended Metro serialization.
|
|
975
|
+
// Keep bundleManager on the local common HBC until this buffered executor
|
|
976
|
+
// proves RCTInstance completed the initial read.
|
|
977
|
+
[instance callFunctionOnBufferedRuntimeExecutor:^(facebook::jsi::Runtime &runtime) {
|
|
978
|
+
dispatch_semaphore_wait(downloadReady, DISPATCH_TIME_FOREVER);
|
|
979
|
+
dispatch_semaphore_wait(hostRegistrationReady, DISPATCH_TIME_FOREVER);
|
|
980
|
+
RCTHost *strongHost = weakHost;
|
|
981
|
+
RCTInstance *strongInstance = weakInstance;
|
|
982
|
+
Ivar currentIvar = strongHost
|
|
983
|
+
? class_getInstanceVariable([strongHost class], "_instance")
|
|
984
|
+
: nil;
|
|
985
|
+
if (!strongHost || !strongInstance || !currentIvar ||
|
|
986
|
+
object_getIvar(strongHost, currentIvar) != strongInstance) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
RCTSource *source = downloadedSource;
|
|
990
|
+
if (downloadError || source.data.length == 0) {
|
|
991
|
+
NSError *loadError = downloadError ?: [NSError errorWithDomain:@"SplitBundleLoader"
|
|
992
|
+
code:1
|
|
993
|
+
userInfo:@{
|
|
994
|
+
NSLocalizedDescriptionKey:
|
|
995
|
+
@"Dev-vendor main delta is empty"
|
|
996
|
+
}];
|
|
997
|
+
[SBLLogger error:[NSString stringWithFormat:
|
|
998
|
+
@"loadDevVendorEntryBundle failed: %@", loadError.localizedDescription]];
|
|
999
|
+
dispatch_async(dispatch_get_main_queue(), ^{ RCTFatal(loadError); });
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
// Keep native reload targeting the live graph after the common HBC is read.
|
|
1003
|
+
strongHost.bundleManager.bundleURL = bundleURL;
|
|
1004
|
+
NSData *data = source.data;
|
|
1005
|
+
NSString *sourceURL = hmrBundleURL.absoluteString;
|
|
1006
|
+
try {
|
|
1007
|
+
facebook::jsi::Value marker = runtime.global().getProperty(
|
|
1008
|
+
runtime, "__ONEKEY_DEV_VENDOR_FINGERPRINT__");
|
|
1009
|
+
if (!marker.isString() ||
|
|
1010
|
+
marker.asString(runtime).utf8(runtime) != std::string(fingerprint.UTF8String)) {
|
|
1011
|
+
throw std::runtime_error("Dev-vendor common fingerprint mismatch");
|
|
1012
|
+
}
|
|
1013
|
+
runtime.global().setProperty(
|
|
1014
|
+
runtime,
|
|
1015
|
+
"__ONEKEY_DEV_VENDOR_FULL_BUNDLE_URL__",
|
|
1016
|
+
facebook::jsi::String::createFromUtf8(runtime, sourceURL.UTF8String));
|
|
1017
|
+
auto buffer = std::make_shared<NSDataJSIBuffer>(data);
|
|
1018
|
+
runtime.evaluateJavaScript(buffer, sourceURL.UTF8String);
|
|
1019
|
+
[SBLLogger info:[NSString stringWithFormat:
|
|
1020
|
+
@"[DevVendor] main common.hbc + Metro delta ready (%lu bytes)",
|
|
1021
|
+
(unsigned long)data.length]];
|
|
1022
|
+
} catch (const std::exception &exception) {
|
|
1023
|
+
NSString *message = [NSString stringWithUTF8String:exception.what()];
|
|
1024
|
+
NSError *evaluationError = [NSError errorWithDomain:@"SplitBundleLoader"
|
|
1025
|
+
code:2
|
|
1026
|
+
userInfo:@{
|
|
1027
|
+
NSLocalizedDescriptionKey:
|
|
1028
|
+
message ?: @"Dev-vendor main delta evaluation failed"
|
|
1029
|
+
}];
|
|
1030
|
+
dispatch_async(dispatch_get_main_queue(), ^{ RCTFatal(evaluationError); });
|
|
1031
|
+
}
|
|
1032
|
+
}];
|
|
1033
|
+
// The initial HBC has a file URL, so RN intentionally skipped HMR setup.
|
|
1034
|
+
// Queue exactly one setup call after the common+delta executor but before
|
|
1035
|
+
// RCTHost queues surface startup. Full reload creates a new runtime and runs
|
|
1036
|
+
// this path again; Fast Refresh reuses the existing HMR client.
|
|
1037
|
+
[reactHost callFunctionOnJSModule:@"HMRClient"
|
|
1038
|
+
method:@"setup"
|
|
1039
|
+
args:@[
|
|
1040
|
+
@"ios",
|
|
1041
|
+
path,
|
|
1042
|
+
hostName,
|
|
1043
|
+
port,
|
|
1044
|
+
@(devSettings.isHotLoadingEnabled),
|
|
1045
|
+
scheme,
|
|
1046
|
+
hmrBundleURL.absoluteString
|
|
1047
|
+
]];
|
|
1048
|
+
[SBLLogger info:@"[DevVendor] main HMR client setup queued"];
|
|
1049
|
+
}
|
|
905
1050
|
// MARK: - loadSegment
|
|
906
1051
|
|
|
907
1052
|
- (void)loadSegment:(double)segmentId
|