From f7219ec02d71d2f0f6c71af4d5c3d4850a898fd8 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Tue, 27 Jun 2023 07:26:16 -0700 Subject: [PATCH] Allow RCTBundleURLProvider to request an inline source map (#37878) Summary: See: http://blog.nparashuram.com/2019/10/debugging-react-native-ios-apps-with.html When using direct debugging with JavaScriptCore, Safari Web Inspector doesn't pick up the source map over the network. Instead, as far as I can tell, it expects you to pass the source URL at the time you load your bundle: https://developer.apple.com/documentation/javascriptcore/jscontext/1451384-evaluatescript?language=objc . This leads to a very sub-par developer experience debugging the JSbundle directly. It will however, pick up an inline source map. Therefore, let's add a way to have React Native tell metro to request an inline source map. I did this by modifying `RCTBundleURLProvider` to have a new query parameter for `inlineSourceMap`, and set to true by default for JSC. ## Changelog: [IOS] [ADDED] - Added support to inline the source map via RCTBundleURLProvider Pull Request resolved: https://github.com/facebook/react-native/pull/37878 Test Plan: I can put a breakpoint in RNTester, via Safari Web Inspector, in human readable code :D Screenshot 2023-06-14 at 4 09 03 AM Reviewed By: motiz88 Differential Revision: D46855418 Pulled By: huntie fbshipit-source-id: 2134cdbcd0a3e81052d26ed75f83601ae4ddecfe --- .../React/Base/RCTBundleURLProvider.h | 35 +++++++- .../React/Base/RCTBundleURLProvider.mm | 86 ++++++++++++++++--- .../RCTBundleURLProviderTests.m | 4 +- 3 files changed, 111 insertions(+), 14 deletions(-) diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.h b/packages/react-native/React/Base/RCTBundleURLProvider.h index cbf8132b1da907..7072e05dd8397b 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.h +++ b/packages/react-native/React/Base/RCTBundleURLProvider.h @@ -101,6 +101,7 @@ RCT_EXTERN void RCTBundleURLProviderAllowPackagerServerAccess(BOOL allowed); @property (nonatomic, assign) BOOL enableMinification; @property (nonatomic, assign) BOOL enableDev; +@property (nonatomic, assign) BOOL inlineSourceMap; /** * The scheme/protocol used of the packager, the default is the http protocol @@ -125,13 +126,32 @@ RCT_EXTERN void RCTBundleURLProviderAllowPackagerServerAccess(BOOL allowed); + (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot packagerHost:(NSString *)packagerHost enableDev:(BOOL)enableDev - enableMinification:(BOOL)enableMinification; + enableMinification:(BOOL)enableMinification + __deprecated_msg( + "Use `jsBundleURLForBundleRoot:packagerHost:enableDev:enableMinification:inlineSourceMap:` instead"); + ++ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot + packagerHost:(NSString *)packagerHost + packagerScheme:(NSString *)scheme + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + modulesOnly:(BOOL)modulesOnly + runModule:(BOOL)runModule + __deprecated_msg( + "Use jsBundleURLForBundleRoot:packagerHost:enableDev:enableMinification:inlineSourceMap:modulesOnly:runModule:` instead"); + ++ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot + packagerHost:(NSString *)packagerHost + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap; + (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot packagerHost:(NSString *)packagerHost packagerScheme:(NSString *)scheme enableDev:(BOOL)enableDev enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap modulesOnly:(BOOL)modulesOnly runModule:(BOOL)runModule; /** @@ -142,6 +162,17 @@ RCT_EXTERN void RCTBundleURLProviderAllowPackagerServerAccess(BOOL allowed); + (NSURL *)resourceURLForResourcePath:(NSString *)path packagerHost:(NSString *)packagerHost scheme:(NSString *)scheme - query:(NSString *)query; + query:(NSString *)query + __deprecated_msg("Use version with queryItems parameter instead"); + +/** + * Given a hostname for the packager and a resource path (including "/"), return the URL to the resource. + * In general, please use the instance method to decide if the packager is running and fallback to the pre-packaged + * resource if it is not: -resourceURLForResourceRoot:resourceName:resourceExtension:offlineBundle: + */ ++ (NSURL *)resourceURLForResourcePath:(NSString *)path + packagerHost:(NSString *)packagerHost + scheme:(NSString *)scheme + queryItems:(NSArray *)queryItems; @end diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.mm b/packages/react-native/React/Base/RCTBundleURLProvider.mm index f1dc797815c87d..7b5dd2b3641feb 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.mm +++ b/packages/react-native/React/Base/RCTBundleURLProvider.mm @@ -22,10 +22,12 @@ void RCTBundleURLProviderAllowPackagerServerAccess(BOOL allowed) kRCTAllowPackagerAccess = allowed; } #endif +static NSString *const kRCTPlatformName = @"ios"; static NSString *const kRCTPackagerSchemeKey = @"RCT_packager_scheme"; static NSString *const kRCTJsLocationKey = @"RCT_jsLocation"; static NSString *const kRCTEnableDevKey = @"RCT_enableDev"; static NSString *const kRCTEnableMinificationKey = @"RCT_enableMinification"; +static NSString *const kRCTInlineSourceMapKey = @"RCT_inlineSourceMap"; @implementation RCTBundleURLProvider @@ -187,6 +189,7 @@ - (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackURLProvider:( packagerScheme:[self packagerScheme] enableDev:[self enableDev] enableMinification:[self enableMinification] + inlineSourceMap:[self inlineSourceMap] modulesOnly:NO runModule:YES]; } @@ -199,6 +202,7 @@ - (NSURL *)jsBundleURLForSplitBundleRoot:(NSString *)bundleRoot packagerScheme:[self packagerScheme] enableDev:[self enableDev] enableMinification:[self enableMinification] + inlineSourceMap:[self inlineSourceMap] modulesOnly:YES runModule:NO]; } @@ -238,13 +242,29 @@ - (NSURL *)resourceURLForResourceRoot:(NSString *)root return [[self class] resourceURLForResourcePath:path packagerHost:packagerServerHostPort scheme:packagerServerScheme - query:nil]; + queryItems:nil]; } + (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot packagerHost:(NSString *)packagerHost enableDev:(BOOL)enableDev enableMinification:(BOOL)enableMinification +{ + return [self jsBundleURLForBundleRoot:bundleRoot + packagerHost:packagerHost + packagerScheme:nil + enableDev:enableDev + enableMinification:enableMinification + inlineSourceMap:NO + modulesOnly:NO + runModule:YES]; +} + ++ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot + packagerHost:(NSString *)packagerHost + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap { return [self jsBundleURLForBundleRoot:bundleRoot @@ -252,6 +272,7 @@ + (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot packagerScheme:nil enableDev:enableDev enableMinification:enableMinification + inlineSourceMap:inlineSourceMap modulesOnly:NO runModule:YES]; } @@ -263,22 +284,43 @@ + (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot enableMinification:(BOOL)enableMinification modulesOnly:(BOOL)modulesOnly runModule:(BOOL)runModule +{ + return [self jsBundleURLForBundleRoot:bundleRoot + packagerHost:packagerHost + packagerScheme:nil + enableDev:enableDev + enableMinification:enableMinification + inlineSourceMap:NO + modulesOnly:modulesOnly + runModule:runModule]; +} + ++ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot + packagerHost:(NSString *)packagerHost + packagerScheme:(NSString *)scheme + enableDev:(BOOL)enableDev + enableMinification:(BOOL)enableMinification + inlineSourceMap:(BOOL)inlineSourceMap + modulesOnly:(BOOL)modulesOnly + runModule:(BOOL)runModule { NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot]; BOOL lazy = enableDev; - // When we support only iOS 8 and above, use queryItems for a better API. - NSString *query = [NSString stringWithFormat:@"platform=ios&dev=%@&lazy=%@&minify=%@&modulesOnly=%@&runModule=%@", - enableDev ? @"true" : @"false", - lazy ? @"true" : @"false", - enableMinification ? @"true" : @"false", - modulesOnly ? @"true" : @"false", - runModule ? @"true" : @"false"]; + NSArray *queryItems = @[ + [[NSURLQueryItem alloc] initWithName:@"platform" value:kRCTPlatformName], + [[NSURLQueryItem alloc] initWithName:@"dev" value:enableDev ? @"true" : @"false"], + [[NSURLQueryItem alloc] initWithName:@"lazy" value:lazy ? @"true" : @"false"], + [[NSURLQueryItem alloc] initWithName:@"minify" value:enableMinification ? @"true" : @"false"], + [[NSURLQueryItem alloc] initWithName:@"inlineSourceMap" value:inlineSourceMap ? @"true" : @"false"], + [[NSURLQueryItem alloc] initWithName:@"modulesOnly" value:modulesOnly ? @"true" : @"false"], + [[NSURLQueryItem alloc] initWithName:@"runModule" value:runModule ? @"true" : @"false"], + ]; NSString *bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey]; if (bundleID) { - query = [NSString stringWithFormat:@"%@&app=%@", query, bundleID]; + queryItems = [queryItems arrayByAddingObject:[[NSURLQueryItem alloc] initWithName:@"app" value:bundleID]]; } - return [[self class] resourceURLForResourcePath:path packagerHost:packagerHost scheme:scheme query:query]; + return [[self class] resourceURLForResourcePath:path packagerHost:packagerHost scheme:scheme queryItems:queryItems]; } + (NSURL *)resourceURLForResourcePath:(NSString *)path @@ -295,6 +337,20 @@ + (NSURL *)resourceURLForResourcePath:(NSString *)path return components.URL; } ++ (NSURL *)resourceURLForResourcePath:(NSString *)path + packagerHost:(NSString *)packagerHost + scheme:(NSString *)scheme + queryItems:(NSArray *)queryItems +{ + NSURLComponents *components = [NSURLComponents componentsWithURL:serverRootWithHostPort(packagerHost, scheme) + resolvingAgainstBaseURL:NO]; + components.path = path; + if (queryItems != nil) { + components.queryItems = queryItems; + } + return components.URL; +} + - (void)updateValue:(id)object forKey:(NSString *)key { [[NSUserDefaults standardUserDefaults] setObject:object forKey:key]; @@ -312,6 +368,11 @@ - (BOOL)enableMinification return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableMinificationKey]; } +- (BOOL)inlineSourceMap +{ + return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTInlineSourceMapKey]; +} + - (NSString *)jsLocation { return [[NSUserDefaults standardUserDefaults] stringForKey:kRCTJsLocationKey]; @@ -341,6 +402,11 @@ - (void)setEnableMinification:(BOOL)enableMinification [self updateValue:@(enableMinification) forKey:kRCTEnableMinificationKey]; } +- (void)setInlineSourceMap:(BOOL)inlineSourceMap +{ + [self updateValue:@(inlineSourceMap) forKey:kRCTInlineSourceMapKey]; +} + - (void)setPackagerScheme:(NSString *)packagerScheme { [self updateValue:packagerScheme forKey:kRCTPackagerSchemeKey]; diff --git a/packages/rn-tester/RNTesterUnitTests/RCTBundleURLProviderTests.m b/packages/rn-tester/RNTesterUnitTests/RCTBundleURLProviderTests.m index 4da7d3fa59d145..596dceb191be1f 100644 --- a/packages/rn-tester/RNTesterUnitTests/RCTBundleURLProviderTests.m +++ b/packages/rn-tester/RNTesterUnitTests/RCTBundleURLProviderTests.m @@ -26,7 +26,7 @@ URLWithString: [NSString stringWithFormat: - @"http://localhost:8081/%@.bundle?platform=ios&dev=true&lazy=true&minify=false&modulesOnly=false&runModule=true&app=com.apple.dt.xctest.tool", + @"http://localhost:8081/%@.bundle?platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=com.apple.dt.xctest.tool", testFile]]; } @@ -36,7 +36,7 @@ URLWithString: [NSString stringWithFormat: - @"http://192.168.1.1:8081/%@.bundle?platform=ios&dev=true&lazy=true&minify=false&modulesOnly=false&runModule=true&app=com.apple.dt.xctest.tool", + @"http://192.168.1.1:8081/%@.bundle?platform=ios&dev=true&lazy=true&minify=false&inlineSourceMap=false&modulesOnly=false&runModule=true&app=com.apple.dt.xctest.tool", testFile]]; }