diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index fa0559ba50cd22..79dd4b0762ff8a 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -9,19 +9,71 @@ @class RCTBridge; -typedef NSURL * (^RCTBridgelessBundleURLGetter)(void); -typedef void (^RCTBridgelessBundleURLSetter)(NSURL *bundleURL); +typedef NSURL *_Nullable (^RCTBridgelessBundleURLGetter)(void); +typedef void (^RCTBridgelessBundleURLSetter)(NSURL *_Nullable bundleURL); +typedef NSMutableArray *_Nullable (^RCTPackagerOptionsUpdater)( + NSMutableArray *_Nullable options); + +/** + * Configuration class for setting up custom bundle locations + */ +@interface RCTBundleConfiguration : NSObject + ++ (nonnull instancetype)defaultConfiguration; + +/** + * The URL of the bundle to load from the file system + */ +@property (nonatomic, readonly, nullable) NSURL *bundleFilePath; + +/** + * The server scheme (e.g. http or https) to use when loading from the packager + */ +@property (nonatomic, readonly, nullable) NSString *packagerServerScheme; + +/** + * The server host (e.g. localhost) to use when loading from the packager + */ +@property (nonatomic, readonly, nullable) NSString *packagerServerHost; + +/** + * A block that modifies the packager options when loading from the packager + */ +@property (nonatomic, copy, nullable) RCTPackagerOptionsUpdater packagerOptionsUpdater; + +/** + * The relative path to the bundle. + */ +@property (nonatomic, readonly, nullable) NSString *bundlePath; + +- (nonnull instancetype)initWithBundleFilePath:(nullable NSURL *)bundleFilePath; + +- (nonnull instancetype)initWithPackagerServerScheme:(nullable NSString *)packagerServerScheme + packagerServerHost:(nullable NSString *)packagerServerHost + bundlePath:(nullable NSString *)bundlePath; + +- (nullable NSURL *)getBundleURL; + +- (nullable NSString *)getPackagerServerScheme; + +- (nullable NSString *)getPackagerServerHost; + +@end /** * A class that allows NativeModules/TurboModules to read/write the bundleURL, with or without the bridge. */ @interface RCTBundleManager : NSObject + +- (nullable instancetype)initWithBundleConfig:(nullable RCTBundleConfiguration *)bundleConfig; + #ifndef RCT_REMOVE_LEGACY_ARCH -- (void)setBridge:(RCTBridge *)bridge; +- (void)setBridge:(nullable RCTBridge *)bridge; #endif // RCT_REMOVE_LEGACY_ARCH -- (void)setBridgelessBundleURLGetter:(RCTBridgelessBundleURLGetter)getter - andSetter:(RCTBridgelessBundleURLSetter)setter - andDefaultGetter:(RCTBridgelessBundleURLGetter)defaultGetter; +- (void)setBridgelessBundleURLGetter:(nullable RCTBridgelessBundleURLGetter)getter + andSetter:(nullable RCTBridgelessBundleURLSetter)setter + andDefaultGetter:(nullable RCTBridgelessBundleURLGetter)defaultGetter; - (void)resetBundleURL; -@property NSURL *bundleURL; +@property (nonatomic, nullable) NSURL *bundleURL; +@property (nonatomic, nonnull) RCTBundleConfiguration *bundleConfig; @end diff --git a/packages/react-native/React/Base/RCTBundleManager.m b/packages/react-native/React/Base/RCTBundleManager.m index baa23d9123653a..bb81de59c83dbf 100644 --- a/packages/react-native/React/Base/RCTBundleManager.m +++ b/packages/react-native/React/Base/RCTBundleManager.m @@ -6,9 +6,93 @@ */ #import "RCTBundleManager.h" +#import #import "RCTAssert.h" #import "RCTBridge+Private.h" #import "RCTBridge.h" +#import "RCTLog.h" + +@implementation RCTBundleConfiguration + ++ (instancetype)defaultConfiguration +{ + return [[self alloc] initWithBundleFilePath:nil packagerServerScheme:nil packagerServerHost:nil bundlePath:nil]; +} + +- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath +{ + return [self initWithBundleFilePath:bundleFilePath packagerServerScheme:nil packagerServerHost:nil bundlePath:nil]; +} + +- (instancetype)initWithPackagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost + bundlePath:(NSString *)bundlePath +{ + return [self initWithBundleFilePath:nil + packagerServerScheme:packagerServerScheme + packagerServerHost:packagerServerHost + bundlePath:bundlePath]; +} + +- (instancetype)initWithBundleFilePath:(NSURL *)bundleFilePath + packagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost + bundlePath:(NSString *)bundlePath +{ + if (self = [super init]) { + _bundleFilePath = bundleFilePath; + _packagerServerScheme = packagerServerScheme; + _packagerServerHost = packagerServerHost; + _bundlePath = bundlePath; + _packagerOptionsUpdater = ^NSMutableArray *(NSMutableArray *options) + { + return options; + }; + } + + return self; +} + +- (NSString *)getPackagerServerScheme +{ + if (!_packagerServerScheme) { + return [[RCTBundleURLProvider sharedSettings] packagerScheme]; + } + + return _packagerServerScheme; +} + +- (NSString *)getPackagerServerHost +{ + if (!_packagerServerHost) { + return [[RCTBundleURLProvider sharedSettings] packagerServerHostPort]; + } + + return _packagerServerHost; +} + +- (NSURL *)getBundleURL +{ + if (_packagerServerScheme && _packagerServerHost) { + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:_bundlePath + packagerServerScheme:_packagerServerScheme + packagerServerHost:_packagerServerHost + packagerOptionsUpdater:_packagerOptionsUpdater]; + } + + if (_bundleFilePath) { + if (!_bundleFilePath.fileURL) { + RCTLogError(@"Bundle file path must be a file URL"); + return nil; + } + + return _bundleFilePath; + } + + return nil; +} + +@end @implementation RCTBundleManager { #ifndef RCT_REMOVE_LEGACY_ARCH @@ -19,6 +103,20 @@ @implementation RCTBundleManager { RCTBridgelessBundleURLGetter _bridgelessBundleURLDefaultGetter; } +- (instancetype)initWithBundleConfig:(RCTBundleConfiguration *)bundleConfig +{ + if (self = [super init]) { + self.bundleConfig = bundleConfig ? bundleConfig : [RCTBundleConfiguration defaultConfiguration]; + } + + return self; +} + +- (instancetype)init +{ + return [self initWithBundleConfig:[RCTBundleConfiguration defaultConfiguration]]; +} + #ifndef RCT_REMOVE_LEGACY_ARCH - (void)setBridge:(RCTBridge *)bridge { diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.h b/packages/react-native/React/Base/RCTBundleURLProvider.h index 3ade62f9c2f2ba..cc060e4d42d23a 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.h +++ b/packages/react-native/React/Base/RCTBundleURLProvider.h @@ -7,6 +7,7 @@ #import +#import "RCTBundleManager.h" #import "RCTDefines.h" RCT_EXTERN NSString *_Nonnull const RCTBundleURLProviderUpdatedNotification; @@ -88,6 +89,16 @@ NS_ASSUME_NONNULL_BEGIN */ - (NSURL *__nullable)jsBundleURLForFallbackExtension:(NSString *__nullable)extension; +/** + * Returns the jsBundleURL for a given bundle entrypoint, + * the packager scheme, server host and options updater + * for modifying default packager options. + */ +- (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot + packagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost + packagerOptionsUpdater:(RCTPackagerOptionsUpdater)packagerOptionsUpdater; + /** * Returns the resourceURL for a given bundle entrypoint and * the fallback offline resource file if the packager is not running. @@ -97,6 +108,19 @@ NS_ASSUME_NONNULL_BEGIN resourceExtension:(NSString *)extension offlineBundle:(NSBundle *)offlineBundle; +/** + * Returns the query items for given options used to create the jsBundleURL. + */ ++ (NSArray *)createJSBundleURLQuery:(NSString *)packagerHost + packagerScheme:(NSString *__nullable)scheme + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap + modulesOnly:(BOOL)modulesOnly + runModule:(BOOL)runModule + additionalOptions: + (NSDictionary *__nullable)additionalOptions; + /** * The IP address or hostname of the packager. */ diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.mm b/packages/react-native/React/Base/RCTBundleURLProvider.mm index 89ccd0c7ad7a58..e99b2dcc1515cf 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.mm +++ b/packages/react-native/React/Base/RCTBundleURLProvider.mm @@ -313,6 +313,54 @@ + (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot additionalOptions:(NSDictionary *__nullable)additionalOptions { NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot]; + NSArray *queryItems = [self createJSBundleURLQuery:packagerHost + packagerScheme:scheme + enableDev:enableDev + enableMinification:enableMinification + inlineSourceMap:inlineSourceMap + modulesOnly:modulesOnly + runModule:runModule + additionalOptions:additionalOptions]; + + return [RCTBundleURLProvider resourceURLForResourcePath:path + packagerHost:packagerHost + scheme:scheme + queryItems:[queryItems copy]]; +} + +- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot + packagerServerScheme:(NSString *)packagerServerScheme + packagerServerHost:(NSString *)packagerServerHost + packagerOptionsUpdater:(RCTPackagerOptionsUpdater)packagerOptionsUpdater +{ + NSArray *queryItems = [RCTBundleURLProvider createJSBundleURLQuery:packagerServerHost + packagerScheme:packagerServerScheme + enableDev:[self enableDev] + enableMinification:[self enableMinification] + inlineSourceMap:[self inlineSourceMap] + modulesOnly:NO + runModule:YES + additionalOptions:nil]; + + NSArray *updatedQueryItems = packagerOptionsUpdater((NSMutableArray *)queryItems); + NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot]; + + return [RCTBundleURLProvider resourceURLForResourcePath:path + packagerHost:packagerServerHost + scheme:packagerServerScheme + queryItems:updatedQueryItems]; +} + ++ (NSArray *)createJSBundleURLQuery:(NSString *)packagerHost + packagerScheme:(NSString *__nullable)scheme + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap + modulesOnly:(BOOL)modulesOnly + runModule:(BOOL)runModule + additionalOptions: + (NSDictionary *__nullable)additionalOptions +{ BOOL lazy = enableDev; NSMutableArray *queryItems = [[NSMutableArray alloc] initWithArray:@[ [[NSURLQueryItem alloc] initWithName:@"platform" value:RCTPlatformName], @@ -345,10 +393,7 @@ + (NSURL *__nullable)jsBundleURLForBundleRoot:(NSString *)bundleRoot } } - return [[self class] resourceURLForResourcePath:path - packagerHost:packagerHost - scheme:scheme - queryItems:[queryItems copy]]; + return queryItems; } + (NSURL *)resourceURLForResourcePath:(NSString *)path