allow fetching any resource under js folder via packager
Summary: This is a simple hook to allow native side to fetch any file under the js root folder via packager. Historically, only the `main.jsbundle` is fetched via the packager. This then allows fetching local file like a json file that lives under the same root js folder Reviewed By: yungsters Differential Revision: D4037730 fbshipit-source-id: a2d6eb5e30d148fee573d413fc4036d0189f4938
This commit is contained in:
parent
4804190be1
commit
150c522be9
|
@ -32,6 +32,15 @@ extern const NSUInteger kRCTBundleURLProviderDefaultPort;
|
|||
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
|
||||
fallbackResource:(NSString *)resourceName;
|
||||
|
||||
/**
|
||||
* Returns the resourceURL for a given bundle entrypoint and
|
||||
* the fallback offline resource file if the packager is not running.
|
||||
*/
|
||||
- (NSURL *)resourceURLForResourceRoot:(NSString *)root
|
||||
resourceName:(NSString *)name
|
||||
resourceExtension:(NSString *)extension
|
||||
offlineBundle:(NSBundle *)offlineBundle;
|
||||
|
||||
/**
|
||||
* Returns the URL of the packager server.
|
||||
*/
|
||||
|
@ -58,4 +67,13 @@ extern const NSUInteger kRCTBundleURLProviderDefaultPort;
|
|||
enableDev:(BOOL)enableDev
|
||||
enableMinification:(BOOL)enableMinification;
|
||||
|
||||
/**
|
||||
* 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
|
||||
query:(NSString *)query;
|
||||
|
||||
@end
|
||||
|
|
|
@ -123,25 +123,52 @@ static NSURL *serverRootWithHost(NSString *host)
|
|||
if (!packagerServerHost) {
|
||||
return [[NSBundle mainBundle] URLForResource:resourceName withExtension:@"jsbundle"];
|
||||
} else {
|
||||
|
||||
return [[self class] jsBundleURLForBundleRoot:bundleRoot
|
||||
packagerHost:packagerServerHost
|
||||
enableDev:[self enableDev]
|
||||
enableMinification:[self enableMinification]];
|
||||
NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot];
|
||||
// When we support only iOS 8 and above, use queryItems for a better API.
|
||||
NSString *query = [NSString stringWithFormat:@"platform=ios&dev=%@&minify=%@",
|
||||
[self enableDev] ? @"true" : @"false",
|
||||
[self enableMinification] ? @"true": @"false"];
|
||||
return [[self class] resourceURLForResourcePath:path packagerHost:packagerServerHost query:query];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSURL *)resourceURLForResourceRoot:(NSString *)root
|
||||
resourceName:(NSString *)name
|
||||
resourceExtension:(NSString *)extension
|
||||
offlineBundle:(NSBundle *)offlineBundle
|
||||
{
|
||||
NSString *packagerServerHost = [self packagerServerHost];
|
||||
if (!packagerServerHost) {
|
||||
// Serve offline bundle (local file)
|
||||
NSBundle *bundle = offlineBundle ?: [NSBundle mainBundle];
|
||||
return [bundle URLForResource:name withExtension:extension];
|
||||
}
|
||||
NSString *path = [NSString stringWithFormat:@"/%@/%@.%@", root, name, extension];
|
||||
return [[self class] resourceURLForResourcePath:path packagerHost:packagerServerHost query:nil];
|
||||
}
|
||||
|
||||
+ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
|
||||
packagerHost:(NSString *)packagerHost
|
||||
enableDev:(BOOL)enableDev
|
||||
enableMinification:(BOOL)enableMinification
|
||||
{
|
||||
NSURLComponents *components = [NSURLComponents componentsWithURL:serverRootWithHost(packagerHost) resolvingAgainstBaseURL:NO];
|
||||
components.path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot];
|
||||
NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot];
|
||||
// When we support only iOS 8 and above, use queryItems for a better API.
|
||||
components.query = [NSString stringWithFormat:@"platform=ios&dev=%@&minify=%@",
|
||||
NSString *query = [NSString stringWithFormat:@"platform=ios&dev=%@&minify=%@",
|
||||
enableDev ? @"true" : @"false",
|
||||
enableMinification ? @"true": @"false"];
|
||||
return [[self class] resourceURLForResourcePath:path packagerHost:packagerHost query:query];
|
||||
}
|
||||
|
||||
+ (NSURL *)resourceURLForResourcePath:(NSString *)path
|
||||
packagerHost:(NSString *)packagerHost
|
||||
query:(NSString *)query
|
||||
{
|
||||
NSURLComponents *components = [NSURLComponents componentsWithURL:serverRootWithHost(packagerHost) resolvingAgainstBaseURL:NO];
|
||||
components.path = path;
|
||||
if (query != nil) {
|
||||
components.query = query;
|
||||
}
|
||||
return components.URL;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ public class DevServerHelper {
|
|||
|
||||
private static final String BUNDLE_URL_FORMAT =
|
||||
"http://%s/%s.bundle?platform=android&dev=%s&hot=%s&minify=%s";
|
||||
private static final String RESOURCE_URL_FORMAT = "http://%s/%s";
|
||||
private static final String SOURCE_MAP_URL_FORMAT =
|
||||
BUNDLE_URL_FORMAT.replaceFirst("\\.bundle", ".map");
|
||||
private static final String LAUNCH_JS_DEVTOOLS_COMMAND_URL_FORMAT =
|
||||
|
@ -217,11 +218,20 @@ public class DevServerHelper {
|
|||
return String.format(Locale.US, BUNDLE_URL_FORMAT, host, jsModulePath, devMode, hmr, jsMinify);
|
||||
}
|
||||
|
||||
private static String createResourceURL(String host, String resourcePath) {
|
||||
return String.format(Locale.US, RESOURCE_URL_FORMAT, host, resourcePath);
|
||||
}
|
||||
|
||||
public void downloadBundleFromURL(
|
||||
final BundleDownloadCallback callback,
|
||||
final String jsModulePath,
|
||||
final File outputFile) {
|
||||
final String bundleURL = createBundleURL(getDebugServerHost(), jsModulePath, getDevMode(), getHMR(), getJSMinifyMode());
|
||||
final String bundleURL = createBundleURL(
|
||||
getDebugServerHost(),
|
||||
jsModulePath,
|
||||
getDevMode(),
|
||||
getHMR(),
|
||||
getJSMinifyMode());
|
||||
final Request request = new Request.Builder()
|
||||
.url(bundleURL)
|
||||
.build();
|
||||
|
@ -449,4 +459,46 @@ public class DevServerHelper {
|
|||
// host itself.
|
||||
return createBundleURL(getHostForJSProxy(), mainModuleName, getDevMode(), getHMR(), getJSMinifyMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a debug-only utility to allow fetching a file via packager.
|
||||
* It's made synchronous for simplicity, but should only be used if it's absolutely
|
||||
* necessary.
|
||||
* @return the file with the fetched content, or null if there's any failure.
|
||||
*/
|
||||
public @Nullable File downloadBundleResourceFromUrlSync(
|
||||
final String resourcePath,
|
||||
final File outputFile) {
|
||||
final String resourceURL = createResourceURL(getDebugServerHost(), resourcePath);
|
||||
final Request request = new Request.Builder()
|
||||
.url(resourceURL)
|
||||
.build();
|
||||
|
||||
try {
|
||||
Response response = mClient.newCall(request).execute();
|
||||
if (!response.isSuccessful()) {
|
||||
return null;
|
||||
}
|
||||
Sink output = null;
|
||||
|
||||
try {
|
||||
output = Okio.sink(outputFile);
|
||||
Okio.buffer(response.body().source()).readAll(output);
|
||||
} finally {
|
||||
if (output != null) {
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
|
||||
return outputFile;
|
||||
} catch (Exception ex) {
|
||||
FLog.e(
|
||||
ReactConstants.TAG,
|
||||
"Failed to fetch resource synchronously - resourcePath: \"%s\", outputFile: \"%s\"",
|
||||
resourcePath,
|
||||
outputFile.getAbsolutePath(),
|
||||
ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ package com.facebook.react.devsupport;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
@ -44,6 +46,9 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
|
|||
void reloadSettings();
|
||||
void handleReloadJS();
|
||||
void isPackagerRunning(DevServerHelper.PackagerStatusCallback callback);
|
||||
@Nullable File downloadBundleResourceFromUrlSync(
|
||||
final String resourceURL,
|
||||
final File outputFile);
|
||||
@Nullable String getLastErrorTitle();
|
||||
@Nullable StackFrame[] getLastErrorStack();
|
||||
}
|
||||
|
|
|
@ -655,6 +655,13 @@ public class DevSupportManagerImpl implements DevSupportManager, PackagerCommand
|
|||
mDevServerHelper.isPackagerRunning(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable File downloadBundleResourceFromUrlSync(
|
||||
final String resourceURL,
|
||||
final File outputFile) {
|
||||
return mDevServerHelper.downloadBundleResourceFromUrlSync(resourceURL, outputFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getLastErrorTitle() {
|
||||
return mLastErrorTitle;
|
||||
|
|
|
@ -11,6 +11,8 @@ package com.facebook.react.devsupport;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
@ -129,6 +131,13 @@ public class DisabledDevSupportManager implements DevSupportManager {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable File downloadBundleResourceFromUrlSync(
|
||||
final String resourceURL,
|
||||
final File outputFile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getLastErrorTitle() {
|
||||
return null;
|
||||
|
|
Loading…
Reference in New Issue