@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.
@@ -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
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onekeyfe/react-native-split-bundle-loader",
3
- "version": "3.0.89",
3
+ "version": "3.0.91",
4
4
  "description": "react-native-split-bundle-loader",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",