Updates from Fri 10 Apr
- Implemented response headers when using `XMLHttpRequest` | Nick Lockwood - [ReactNative] Don't redbox on flow config errors | Spencer Ahrens - [ReactNative] Fix suggestions in TextInput when setting value prop | Spencer Ahrens - Link LinkingIOS in SampleApp | Nick Lockwood - unify use of password and secureTextEntry for TextInput | Nick Lockwood - Added random js queue+execution time sampling in react native | Bryce Redd - [react_native] JS files from D1980312: [react_native] Fix webview | Andrei Coman - [react-packager] Correct module extension regexp | Amjad Masad - [react-packager] Implement the browser field package.json spec | Amjad Masad - [ReactNative] Bring back crash reporting | Alex Kotliarskyi - Remove duplicate word | Nick Lockwood - NavigatorIOS navigationBarHidden property support | Nick Lockwood - [Scroll] Include content insets in scroll events | Nick Lockwood
This commit is contained in:
parent
7047d6e191
commit
096f1d9c4c
|
@ -15,6 +15,7 @@
|
|||
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
|
||||
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
|
@ -80,6 +81,13 @@
|
|||
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
|
||||
remoteInfo = React;
|
||||
};
|
||||
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTLinking;
|
||||
};
|
||||
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
|
||||
|
@ -91,7 +99,6 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocketDebugger.xcodeproj; path = ../../Libraries/RCTWebSocketDebugger/RCTWebSocketDebugger.xcodeproj; sourceTree = "<group>"; };
|
||||
00481BE91AC0C89D00671115 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
|
||||
008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = main.jsbundle; path = iOS/main.jsbundle; sourceTree = "<group>"; };
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
00C302AF1ABCB8E700DB3ED1 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -107,6 +114,7 @@
|
|||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = iOS/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = iOS/main.m; sourceTree = "<group>"; };
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = "<group>"; };
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
|
@ -121,6 +129,7 @@
|
|||
00C302E61ABCBA2D00DB3ED1 /* libRCTAdSupport.a in Frameworks */,
|
||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
|
||||
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */,
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */,
|
||||
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */,
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */,
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */,
|
||||
|
@ -208,9 +217,18 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
78C398B11ACF4ADC00677621 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78C398B91ACF4ADC00677621 /* libRCTLinking.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */,
|
||||
146833FF1AC3E56700842450 /* React.xcodeproj */,
|
||||
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */,
|
||||
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
|
||||
|
@ -220,7 +238,6 @@
|
|||
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */,
|
||||
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */,
|
||||
00481BDB1AC0C7FA00671115 /* RCTWebSocketDebugger.xcodeproj */,
|
||||
00481BE91AC0C89D00671115 /* libicucore.dylib */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
@ -309,6 +326,10 @@
|
|||
ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 78C398B11ACF4ADC00677621 /* Products */;
|
||||
ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */;
|
||||
ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */;
|
||||
|
@ -394,6 +415,13 @@
|
|||
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTLinking.a;
|
||||
remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
|
|
@ -251,7 +251,7 @@ exports.examples = [
|
|||
return (
|
||||
<View>
|
||||
<WithLabel label="true">
|
||||
<TextInput secureTextEntry={true} style={styles.default} value="abc" />
|
||||
<TextInput password={true} style={styles.default} value="abc" />
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -50,6 +50,7 @@ var RCTNavigatorItem = createReactIOSNativeComponentClass({
|
|||
rightButtonTitle: true,
|
||||
onNavRightButtonTap: true,
|
||||
tintColor: true,
|
||||
navigationBarHidden: true,
|
||||
backButtonTitle: true,
|
||||
titleTextColor: true,
|
||||
style: true,
|
||||
|
@ -235,6 +236,11 @@ var NavigatorIOS = React.createClass({
|
|||
|
||||
}).isRequired,
|
||||
|
||||
/**
|
||||
* A Boolean value that indicates whether the navigation bar is hidden
|
||||
*/
|
||||
navigationBarHidden: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The default wrapper style for components in the navigator.
|
||||
* A common use case is to set the backgroundColor for every page
|
||||
|
@ -547,6 +553,7 @@ var NavigatorIOS = React.createClass({
|
|||
backButtonTitle={route.backButtonTitle}
|
||||
rightButtonTitle={route.rightButtonTitle}
|
||||
onNavRightButtonTap={route.onRightButtonPress}
|
||||
navigationBarHidden={this.props.navigationBarHidden}
|
||||
tintColor={this.props.tintColor}>
|
||||
<Component
|
||||
navigator={this.navigator}
|
||||
|
|
|
@ -198,12 +198,6 @@ var TextInput = React.createClass({
|
|||
* automatically enables it when there is text. Default value is false.
|
||||
*/
|
||||
enablesReturnKeyAutomatically: PropTypes.bool,
|
||||
|
||||
/**
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. Default value is false.
|
||||
*/
|
||||
secureTextEntry: PropTypes.bool,
|
||||
/**
|
||||
* If true, the text input can be multiple lines. Default value is false.
|
||||
*/
|
||||
|
@ -221,11 +215,17 @@ var TextInput = React.createClass({
|
|||
*/
|
||||
onChange: PropTypes.func,
|
||||
onChangeText: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Callback that is called when text input ends.
|
||||
*/
|
||||
onEndEditing: PropTypes.func,
|
||||
/**
|
||||
* Callback that is called when the text input's submit button is pressed.
|
||||
*/
|
||||
onSubmitEditing: PropTypes.func,
|
||||
/**
|
||||
* If true, the TextInput will be a password field. Default value is false.
|
||||
* If true, the text input obscures the text entered so that sensitive text
|
||||
* like passwords stay secure. Default value is false.
|
||||
*/
|
||||
password: PropTypes.bool,
|
||||
/**
|
||||
|
@ -419,7 +419,7 @@ var TextInput = React.createClass({
|
|||
keyboardType={keyboardType}
|
||||
returnKeyType={returnKeyType}
|
||||
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
|
||||
secureTextEntry={this.props.secureTextEntry}
|
||||
secureTextEntry={this.props.password || this.props.secureTextEntry}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
|
@ -464,7 +464,7 @@ var TextInput = React.createClass({
|
|||
keyboardType={keyboardType}
|
||||
returnKeyType={returnKeyType}
|
||||
enablesReturnKeyAutomatically={this.props.enablesReturnKeyAutomatically}
|
||||
secureTextEntry={this.props.secureTextEntry}
|
||||
secureTextEntry={this.props.password || this.props.secureTextEntry}
|
||||
onFocus={this._onFocus}
|
||||
onBlur={this._onBlur}
|
||||
onChange={this._onChange}
|
||||
|
@ -505,7 +505,7 @@ var TextInput = React.createClass({
|
|||
onChange={this._onChange}
|
||||
onEndEditing={this.props.onEndEditing}
|
||||
onSubmitEditing={this.props.onSubmitEditing}
|
||||
password={this.props.password}
|
||||
password={this.props.password || this.props.secureTextEntry}
|
||||
placeholder={this.props.placeholder}
|
||||
value={this.state.bufferedValue}
|
||||
/>;
|
||||
|
|
|
@ -34,8 +34,8 @@ var WebViewState = keyMirror({
|
|||
var WebView = React.createClass({
|
||||
|
||||
propTypes: {
|
||||
renderError: PropTypes.func.isRequired, // view to show if there's an error
|
||||
renderLoading: PropTypes.func.isRequired, // loading indicator to show
|
||||
renderError: PropTypes.func, // view to show if there's an error
|
||||
renderLoading: PropTypes.func, // loading indicator to show
|
||||
url: PropTypes.string.isRequired,
|
||||
automaticallyAdjustContentInsets: PropTypes.bool,
|
||||
contentInset: EdgeInsetsPropType,
|
||||
|
@ -66,10 +66,10 @@ var WebView = React.createClass({
|
|||
var otherView = null;
|
||||
|
||||
if (this.state.viewState === WebViewState.LOADING) {
|
||||
otherView = this.props.renderLoading();
|
||||
otherView = this.props.renderLoading && this.props.renderLoading();
|
||||
} else if (this.state.viewState === WebViewState.ERROR) {
|
||||
var errorEvent = this.state.lastErrorEvent;
|
||||
otherView = this.props.renderError(
|
||||
otherView = this.props.renderError && this.props.renderError(
|
||||
errorEvent.domain,
|
||||
errorEvent.code,
|
||||
errorEvent.description);
|
||||
|
|
|
@ -13,6 +13,9 @@
|
|||
|
||||
@interface RCTLinkingManager : NSObject <RCTBridgeModule>
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)URL
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation;
|
||||
|
||||
@end
|
||||
|
|
|
@ -37,11 +37,11 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
|
||||
+ (BOOL)application:(UIApplication *)application
|
||||
openURL:(NSURL *)url
|
||||
openURL:(NSURL *)URL
|
||||
sourceApplication:(NSString *)sourceApplication
|
||||
annotation:(id)annotation
|
||||
{
|
||||
NSDictionary *payload = @{@"url": [url absoluteString]};
|
||||
NSDictionary *payload = @{@"url": [URL absoluteString]};
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification
|
||||
object:self
|
||||
userInfo:payload];
|
||||
|
@ -54,23 +54,22 @@ RCT_EXPORT_MODULE()
|
|||
body:[notification userInfo]];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(openURL:(NSURL *)url)
|
||||
RCT_EXPORT_METHOD(openURL:(NSURL *)URL)
|
||||
{
|
||||
[[UIApplication sharedApplication] openURL:url];
|
||||
[[UIApplication sharedApplication] openURL:URL];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(canOpenURL:(NSURL *)url
|
||||
RCT_EXPORT_METHOD(canOpenURL:(NSURL *)URL
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
BOOL supported = [[UIApplication sharedApplication] canOpenURL:url];
|
||||
callback(@[@(supported)]);
|
||||
BOOL canOpen = [[UIApplication sharedApplication] canOpenURL:URL];
|
||||
callback(@[@(canOpen)]);
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{
|
||||
@"initialURL": [[_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsURLKey] absoluteString] ?: [NSNull null]
|
||||
};
|
||||
NSURL *initialURL = _bridge.launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
return @{@"initialURL": [initialURL absoluteString] ?: [NSNull null]};
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "RCTDataManager.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -22,27 +23,18 @@ RCT_EXPORT_MODULE()
|
|||
* The responseSender block won't be called on same thread as called.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(queryData:(NSString *)queryType
|
||||
withQuery:(id)query
|
||||
withQuery:(NSDictionary *)query
|
||||
queryHash:(__unused NSString *)queryHash
|
||||
responseSender:(RCTResponseSenderBlock)responseSender)
|
||||
{
|
||||
if ([queryType isEqualToString:@"http"]) {
|
||||
|
||||
// Parse query
|
||||
NSDictionary *queryDict = query;
|
||||
if ([query isKindOfClass:[NSString class]]) {
|
||||
// TODO: it would be more efficient just to send a dictionary
|
||||
queryDict = RCTJSONParse(query, NULL);
|
||||
}
|
||||
|
||||
// Build request
|
||||
NSURL *url = [NSURL URLWithString:queryDict[@"url"]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
request.HTTPMethod = queryDict[@"method"] ?: @"GET";
|
||||
request.allHTTPHeaderFields = queryDict[@"headers"];
|
||||
if ([queryDict[@"data"] isKindOfClass:[NSString class]]) {
|
||||
request.HTTPBody = [queryDict[@"data"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
NSURL *URL = [RCTConvert NSURL:query[@"url"]];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
|
||||
request.HTTPMethod = [RCTConvert NSString:query[@"method"]] ?: @"GET";
|
||||
request.allHTTPHeaderFields = [RCTConvert NSDictionary:query[@"headers"]];
|
||||
request.HTTPBody = [RCTConvert NSData:query[@"data"]];
|
||||
|
||||
// Build data task
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
|
||||
|
@ -50,18 +42,27 @@ RCT_EXPORT_METHOD(queryData:(NSString *)queryType
|
|||
// Build response
|
||||
NSDictionary *responseJSON;
|
||||
if (connectionError == nil) {
|
||||
NSStringEncoding encoding;
|
||||
NSStringEncoding encoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
} else {
|
||||
encoding = NSUTF8StringEncoding;
|
||||
}
|
||||
int responseCode = (int)[((NSHTTPURLResponse *)response) statusCode];
|
||||
NSString *returnData = [[NSString alloc] initWithData:data encoding:encoding];
|
||||
responseJSON = @{@"status": @(responseCode), @"responseText": returnData};
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
}
|
||||
responseJSON = @{
|
||||
@"status": @([httpResponse statusCode] ?: 200),
|
||||
@"responseHeaders": [httpResponse allHeaderFields] ?: @{},
|
||||
@"responseText": [[NSString alloc] initWithData:data encoding:encoding] ?: @""
|
||||
};
|
||||
} else {
|
||||
responseJSON = @{@"status": @0, @"responseText": [connectionError localizedDescription]};
|
||||
responseJSON = @{
|
||||
@"status": @0,
|
||||
@"responseHeaders": @{},
|
||||
@"responseText": [connectionError localizedDescription]
|
||||
};
|
||||
}
|
||||
|
||||
// Send response (won't be sent on same thread as caller)
|
||||
|
|
|
@ -22,12 +22,13 @@ class XMLHttpRequest extends XMLHttpRequestBase {
|
|||
sendImpl(method: ?string, url: ?string, headers: Object, data: any): void {
|
||||
RCTDataManager.queryData(
|
||||
'http',
|
||||
JSON.stringify({
|
||||
{
|
||||
method: method,
|
||||
url: url,
|
||||
data: data,
|
||||
headers: headers,
|
||||
}),
|
||||
},
|
||||
// TODO: Do we need this? is it used anywhere?
|
||||
'h' + crc32(method + '|' + url + '|' + data),
|
||||
(result) => {
|
||||
result = JSON.parse(result);
|
||||
|
|
|
@ -85,51 +85,31 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback
|
|||
|
||||
RCT_EXPORT_METHOD(requestPermissions)
|
||||
{
|
||||
Class _UIUserNotificationSettings;
|
||||
if ((_UIUserNotificationSettings = NSClassFromString(@"UIUserNotificationSettings"))) {
|
||||
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
|
||||
UIUserNotificationSettings *notificationSettings = [_UIUserNotificationSettings settingsForTypes:types categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
|
||||
} else {
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
|
||||
|
||||
// if we are targeting iOS 7, *and* the new UIUserNotificationSettings
|
||||
// class is not available, then register using the old mechanism
|
||||
if (![UIUserNotificationSettings class]) {
|
||||
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
|
||||
UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert];
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert;
|
||||
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
|
||||
|
||||
#define UIUserNotificationTypeAlert UIRemoteNotificationTypeAlert
|
||||
#define UIUserNotificationTypeBadge UIRemoteNotificationTypeBadge
|
||||
#define UIUserNotificationTypeSound UIRemoteNotificationTypeSound
|
||||
|
||||
#endif
|
||||
|
||||
NSUInteger types;
|
||||
if ([UIApplication instancesRespondToSelector:@selector(currentUserNotificationSettings)]) {
|
||||
types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
|
||||
} else {
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
|
||||
|
||||
types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
NSMutableDictionary *permissions = [[NSMutableDictionary alloc] init];
|
||||
permissions[@"alert"] = @((types & UIUserNotificationTypeAlert) > 0);
|
||||
permissions[@"badge"] = @((types & UIUserNotificationTypeBadge) > 0);
|
||||
permissions[@"sound"] = @((types & UIUserNotificationTypeSound) > 0);
|
||||
|
||||
UIUserNotificationType types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
|
||||
permissions[@"alert"] = @((BOOL)(types & UIUserNotificationTypeAlert));
|
||||
permissions[@"badge"] = @((BOOL)(types & UIUserNotificationTypeBadge));
|
||||
permissions[@"sound"] = @((BOOL)(types & UIUserNotificationTypeSound));
|
||||
|
||||
callback(@[permissions]);
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ var StyleSheetValidation = require('StyleSheetValidation');
|
|||
* Code quality:
|
||||
*
|
||||
* - By moving styles away from the render function, you're making the code
|
||||
* code easier to understand.
|
||||
* easier to understand.
|
||||
* - Naming the styles is a good way to add meaning to the low level components
|
||||
* in the render function.
|
||||
*
|
||||
|
|
|
@ -49,6 +49,9 @@ typedef struct section RCTHeaderSection;
|
|||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#endif
|
||||
|
||||
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
||||
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
|
@ -926,10 +929,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"JS_PERF_ENQUEUE" object:nil userInfo:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
||||
|
||||
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:@"JS_PERF_DEQUEUE" object:nil userInfo:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
||||
[self _handleBuffer:json];
|
||||
};
|
||||
|
||||
|
@ -1038,6 +1041,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, module, params, exception);
|
||||
if ([exception.name rangeOfString:@"Unhandled JS Exception"].location != NSNotFound) {
|
||||
@throw;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
+ (NSDictionary *)NSDictionary:(id)json;
|
||||
+ (NSString *)NSString:(id)json;
|
||||
+ (NSNumber *)NSNumber:(id)json;
|
||||
+ (NSData *)NSData:(id)json;
|
||||
|
||||
+ (NSURL *)NSURL:(id)json;
|
||||
+ (NSURLRequest *)NSURLRequest:(id)json;
|
||||
|
|
|
@ -50,6 +50,12 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
return nil;
|
||||
}
|
||||
|
||||
+ (NSData *)NSData:(id)json
|
||||
{
|
||||
// TODO: should we automatically decode base64 data? Probably not...
|
||||
return [[self NSString:json] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
+ (NSURL *)NSURL:(id)json
|
||||
{
|
||||
if (![json isKindOfClass:[NSString class]]) {
|
||||
|
|
|
@ -97,6 +97,12 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
|
|||
@"x": @(scrollView.contentOffset.x),
|
||||
@"y": @(scrollView.contentOffset.y)
|
||||
},
|
||||
@"contentInset": @{
|
||||
@"top": @(scrollView.contentInset.top),
|
||||
@"left": @(scrollView.contentInset.left),
|
||||
@"bottom": @(scrollView.contentInset.bottom),
|
||||
@"right": @(scrollView.contentInset.right)
|
||||
},
|
||||
@"contentSize": @{
|
||||
@"width": @(scrollView.contentSize.width),
|
||||
@"height": @(scrollView.contentSize.height)
|
||||
|
|
|
@ -21,4 +21,6 @@
|
|||
|
||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, assign) NSUInteger maxReloadAttempts;
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,19 +9,27 @@
|
|||
|
||||
#import "RCTExceptionsManager.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTRootView.h"
|
||||
|
||||
@implementation RCTExceptionsManager
|
||||
{
|
||||
__weak id<RCTExceptionsManagerDelegate> _delegate;
|
||||
NSUInteger _reloadRetries;
|
||||
}
|
||||
|
||||
#ifndef DEBUG
|
||||
static NSUInteger RCTReloadRetries = 0;
|
||||
#endif
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)initWithDelegate:(id<RCTExceptionsManagerDelegate>)delegate
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_delegate = delegate;
|
||||
_maxReloadAttempts = 0;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -36,9 +44,40 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
|
|||
{
|
||||
if (_delegate) {
|
||||
[_delegate unhandledJSExceptionWithMessage:message stack:stack];
|
||||
} else {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
||||
#else
|
||||
if (RCTReloadRetries < _maxReloadAttempts) {
|
||||
RCTReloadRetries++;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification object:nil];
|
||||
});
|
||||
} else {
|
||||
NSError *error;
|
||||
const NSUInteger MAX_SANITIZED_LENGTH = 75;
|
||||
// Filter out numbers so the same base errors are mapped to the same categories independent of incorrect values.
|
||||
NSString *pattern = @"[+-]?\\d+[,.]?\\d*";
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
|
||||
RCTAssert(error == nil, @"Bad regex pattern: %@", pattern);
|
||||
NSString *sanitizedMessage = [regex stringByReplacingMatchesInString:message
|
||||
options:0
|
||||
range:NSMakeRange(0, message.length)
|
||||
withTemplate:@"<num>"];
|
||||
if (sanitizedMessage.length > MAX_SANITIZED_LENGTH) {
|
||||
sanitizedMessage = [[sanitizedMessage substringToIndex:MAX_SANITIZED_LENGTH] stringByAppendingString:@"..."];
|
||||
}
|
||||
NSMutableString *prettyStack = [@"\n" mutableCopy];
|
||||
for (NSDictionary *frame in stack) {
|
||||
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
||||
}
|
||||
|
||||
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage];
|
||||
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(updateExceptionMessage:(NSString *)message
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
@property (nonatomic, copy) NSString *title;
|
||||
@property (nonatomic, copy) NSString *rightButtonTitle;
|
||||
@property (nonatomic, copy) NSString *backButtonTitle;
|
||||
@property (nonatomic, assign) BOOL navigationBarHidden;
|
||||
@property (nonatomic, copy) UIColor *tintColor;
|
||||
@property (nonatomic, copy) UIColor *barTintColor;
|
||||
@property (nonatomic, copy) UIColor *titleTextColor;
|
||||
|
|
|
@ -24,6 +24,7 @@ RCT_EXPORT_MODULE()
|
|||
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
|
||||
RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString);
|
||||
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL);
|
||||
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor);
|
||||
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor);
|
||||
RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor);
|
||||
|
|
|
@ -35,6 +35,13 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
if (![text isEqualToString:self.text]) {
|
||||
[super setText:text];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray *)reactSubviews
|
||||
{
|
||||
// TODO: do we support subviews of textfield in React?
|
||||
|
|
|
@ -65,7 +65,10 @@
|
|||
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
|
||||
{
|
||||
|
||||
[self.navigationController setNavigationBarHidden:!_navItem animated:animated];
|
||||
[self.navigationController
|
||||
setNavigationBarHidden:_navItem.navigationBarHidden
|
||||
animated:animated];
|
||||
|
||||
if (!_navItem) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-native",
|
||||
"version": "0.3.9",
|
||||
"version": "0.3.7",
|
||||
"description": "A framework for building native apps using React",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var chalk = require('chalk');
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
var hasWarned = {};
|
||||
|
||||
function getFlowTypeCheckMiddleware(options) {
|
||||
return function(req, res, next) {
|
||||
if (options.skipflow) {
|
||||
|
@ -18,13 +21,19 @@ function getFlowTypeCheckMiddleware(options) {
|
|||
if (options.flowroot || options.projectRoots.length === 1) {
|
||||
var flowroot = options.flowroot || options.projectRoots[0];
|
||||
} else {
|
||||
if (!hasWarned.noRoot) {
|
||||
hasWarned.noRoot = true;
|
||||
console.warn('flow: No suitable root');
|
||||
}
|
||||
return next();
|
||||
}
|
||||
exec('command -v flow >/dev/null 2>&1', function(error, stdout) {
|
||||
if (error) {
|
||||
console.warn('flow: Skipping because not installed. Install with ' +
|
||||
'`brew install flow`.');
|
||||
if (!hasWarned.noFlow) {
|
||||
hasWarned.noFlow = true;
|
||||
console.warn(chalk.yellow('flow: Skipping because not installed. Install with ' +
|
||||
'`brew install flow`.'));
|
||||
}
|
||||
return next();
|
||||
} else {
|
||||
return doFlowTypecheck(res, flowroot, next);
|
||||
|
@ -34,16 +43,21 @@ function getFlowTypeCheckMiddleware(options) {
|
|||
}
|
||||
|
||||
function doFlowTypecheck(res, flowroot, next) {
|
||||
// vjeux: big hack to make it work on the sample app because we don't generate a
|
||||
// .flowconfig in the init script right now.
|
||||
return next();
|
||||
|
||||
var flowCmd = 'cd "' + flowroot + '" && flow --json --timeout 20';
|
||||
var start = Date.now();
|
||||
console.log('flow: Running static typechecks.');
|
||||
exec(flowCmd, function(flowError, stdout) {
|
||||
// Log start message if flow is slow to let user know something is happening.
|
||||
var flowSlow = setTimeout(
|
||||
function() {
|
||||
console.log(chalk.gray('flow: Running static typechecks.'));
|
||||
},
|
||||
500
|
||||
);
|
||||
exec(flowCmd, function(flowError, stdout, stderr) {
|
||||
clearTimeout(flowSlow);
|
||||
if (!flowError) {
|
||||
console.log('flow: Typechecks passed (' + (Date.now() - start) + 'ms).');
|
||||
console.log(chalk.gray(
|
||||
'flow: Typechecks passed (' + (Date.now() - start) + 'ms).')
|
||||
);
|
||||
return next();
|
||||
} else {
|
||||
try {
|
||||
|
@ -65,24 +79,38 @@ function doFlowTypecheck(res, flowroot, next) {
|
|||
});
|
||||
errorNum++;
|
||||
});
|
||||
var message = 'Flow found type errors. If you think these are wrong, ' +
|
||||
'make sure flow is up to date, or disable with --skipflow.';
|
||||
} catch (e) {
|
||||
var message =
|
||||
'Flow failed to provide parseable output:\n\n`' + stdout + '`';
|
||||
console.error(message, '\nException: `', e, '`\n\n');
|
||||
}
|
||||
var error = {
|
||||
status: 500,
|
||||
message: message,
|
||||
message: 'Flow found type errors. If you think these are wrong, ' +
|
||||
'make sure your flow bin and .flowconfig are up to date, or ' +
|
||||
'disable with --skipflow.',
|
||||
type: 'FlowError',
|
||||
errors: errors,
|
||||
};
|
||||
console.error('flow: Error running command `' + flowCmd + '`:\n', error);
|
||||
console.error(chalk.yellow('flow: Error running command `' + flowCmd +
|
||||
'`:\n' + JSON.stringify(error))
|
||||
);
|
||||
res.writeHead(error.status, {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
});
|
||||
res.end(JSON.stringify(error));
|
||||
} catch (e) {
|
||||
if (stderr.match(/Could not find a \.flowconfig/)) {
|
||||
if (!hasWarned.noConfig) {
|
||||
hasWarned.noConfig = true;
|
||||
console.warn(chalk.yellow('flow: ' + stderr));
|
||||
}
|
||||
} else {
|
||||
if (!hasWarned.brokenFlow) {
|
||||
hasWarned.brokenFlow = true;
|
||||
console.warn(chalk.yellow(
|
||||
'Flow failed to provide parseable output:\n\n`' + stdout +
|
||||
'`.\n' + 'stderr: `' + stderr + '`'
|
||||
));
|
||||
}
|
||||
}
|
||||
return next();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -64,6 +64,42 @@ describe('DependencyGraph', function() {
|
|||
});
|
||||
});
|
||||
|
||||
pit('should get dependencies with the correct extensions', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("a")'
|
||||
].join('\n'),
|
||||
'a.js': [
|
||||
'/**',
|
||||
' * @providesModule a',
|
||||
' */',
|
||||
].join('\n'),
|
||||
'a.js.orig': [
|
||||
'/**',
|
||||
' * @providesModule a',
|
||||
' */',
|
||||
].join('\n'),
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{id: 'index', altId: '/root/index.js', path: '/root/index.js', dependencies: ['a']},
|
||||
{id: 'a', altId: '/root/a.js', path: '/root/a.js', dependencies: []},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should get dependencies with deprecated assets', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
|
@ -674,6 +710,296 @@ describe('DependencyGraph', function() {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should support simple browser field in packages', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("aPackage")',
|
||||
].join('\n'),
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
main: 'main.js',
|
||||
browser: 'client.js',
|
||||
}),
|
||||
'main.js': 'some other code',
|
||||
'client.js': 'some code',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['aPackage']
|
||||
},
|
||||
{ id: 'aPackage/client',
|
||||
path: '/root/aPackage/client.js',
|
||||
dependencies: []
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should supportbrowser field in packages w/o .js ext', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("aPackage")',
|
||||
].join('\n'),
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
main: 'main.js',
|
||||
browser: 'client',
|
||||
}),
|
||||
'main.js': 'some other code',
|
||||
'client.js': 'some code',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['aPackage']
|
||||
},
|
||||
{ id: 'aPackage/client',
|
||||
path: '/root/aPackage/client.js',
|
||||
dependencies: []
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should support mapping main in browser field json', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("aPackage")',
|
||||
].join('\n'),
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
main: './main.js',
|
||||
browser: {
|
||||
'./main.js': './client.js',
|
||||
},
|
||||
}),
|
||||
'main.js': 'some other code',
|
||||
'client.js': 'some code',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['aPackage']
|
||||
},
|
||||
{ id: 'aPackage/client',
|
||||
path: '/root/aPackage/client.js',
|
||||
dependencies: []
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should work do correct browser mapping w/o js ext', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("aPackage")',
|
||||
].join('\n'),
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
main: './main.js',
|
||||
browser: {
|
||||
'./main': './client.js',
|
||||
},
|
||||
}),
|
||||
'main.js': 'some other code',
|
||||
'client.js': 'some code',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['aPackage']
|
||||
},
|
||||
{ id: 'aPackage/client',
|
||||
path: '/root/aPackage/client.js',
|
||||
dependencies: []
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should support browser mapping of files', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("aPackage")',
|
||||
].join('\n'),
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
main: './main.js',
|
||||
browser: {
|
||||
'./main': './client.js',
|
||||
'./node.js': './not-node.js',
|
||||
'./not-browser': './browser.js',
|
||||
'./dir/server.js': './dir/client',
|
||||
},
|
||||
}),
|
||||
'main.js': 'some other code',
|
||||
'client.js': 'require("./node")\nrequire("./dir/server.js")',
|
||||
'not-node.js': 'require("./not-browser")',
|
||||
'not-browser.js': 'require("./dir/server")',
|
||||
'browser.js': 'some browser code',
|
||||
'dir': {
|
||||
'server.js': 'some node code',
|
||||
'client.js': 'some browser code',
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['aPackage']
|
||||
},
|
||||
{ id: 'aPackage/client',
|
||||
path: '/root/aPackage/client.js',
|
||||
dependencies: ['./node', './dir/server.js']
|
||||
},
|
||||
{ id: 'aPackage/not-node',
|
||||
path: '/root/aPackage/not-node.js',
|
||||
dependencies: ['./not-browser']
|
||||
},
|
||||
{ id: 'aPackage/browser',
|
||||
path: '/root/aPackage/browser.js',
|
||||
dependencies: []
|
||||
},
|
||||
{ id: 'aPackage/dir/client',
|
||||
path: '/root/aPackage/dir/client.js',
|
||||
dependencies: []
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should support browser mapping for packages', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("aPackage")',
|
||||
].join('\n'),
|
||||
'aPackage': {
|
||||
'package.json': JSON.stringify({
|
||||
name: 'aPackage',
|
||||
browser: {
|
||||
'node-package': 'browser-package',
|
||||
}
|
||||
}),
|
||||
'index.js': 'require("node-package")',
|
||||
'node-package': {
|
||||
'package.json': JSON.stringify({
|
||||
'name': 'node-package',
|
||||
}),
|
||||
'index.js': 'some node code',
|
||||
},
|
||||
'browser-package': {
|
||||
'package.json': JSON.stringify({
|
||||
'name': 'browser-package',
|
||||
}),
|
||||
'index.js': 'some browser code',
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
.toEqual([
|
||||
{ id: 'index', altId: '/root/index.js',
|
||||
path: '/root/index.js',
|
||||
dependencies: ['aPackage']
|
||||
},
|
||||
{ id: 'aPackage/index',
|
||||
path: '/root/aPackage/index.js',
|
||||
dependencies: ['node-package']
|
||||
},
|
||||
{ id: 'browser-package/index',
|
||||
path: '/root/aPackage/browser-package/index.js',
|
||||
dependencies: []
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('file watch updating', function() {
|
||||
|
|
|
@ -65,7 +65,7 @@ function DependecyGraph(options) {
|
|||
this._debugUpdateEvents = [];
|
||||
|
||||
this._moduleExtPattern = new RegExp(
|
||||
'.' + ['js'].concat(this._assetExts).join('|') + '$'
|
||||
'\.(' + ['js'].concat(this._assetExts).join('|') + ')$'
|
||||
);
|
||||
|
||||
// Kick off the search process to precompute the dependency graph.
|
||||
|
@ -168,15 +168,22 @@ DependecyGraph.prototype.resolveDependency = function(
|
|||
// Package relative modules starts with '.' or '..'.
|
||||
if (depModuleId[0] !== '.') {
|
||||
|
||||
// 1. `depModuleId` is simply a top-level `providesModule`.
|
||||
// 2. `depModuleId` is a package module but given the full path from the
|
||||
// Check if we need to map the dependency to something else via the
|
||||
// `browser` field in package.json
|
||||
var fromPackageJson = this._lookupPackage(fromModule.path);
|
||||
if (fromPackageJson && fromPackageJson.browser &&
|
||||
fromPackageJson.browser[depModuleId]) {
|
||||
depModuleId = fromPackageJson.browser[depModuleId];
|
||||
}
|
||||
|
||||
// `depModuleId` is simply a top-level `providesModule`.
|
||||
// `depModuleId` is a package module but given the full path from the
|
||||
// package, i.e. package_name/module_name
|
||||
if (this._moduleById[sansExtJs(depModuleId)]) {
|
||||
return this._moduleById[sansExtJs(depModuleId)];
|
||||
}
|
||||
|
||||
// 3. `depModuleId` is a package and it's depending on the "main"
|
||||
// resolution.
|
||||
// `depModuleId` is a package and it's depending on the "main" resolution.
|
||||
packageJson = this._packagesById[depModuleId];
|
||||
|
||||
// We are being forgiving here and raising an error because we could be
|
||||
|
@ -190,7 +197,25 @@ DependecyGraph.prototype.resolveDependency = function(
|
|||
return null;
|
||||
}
|
||||
|
||||
var main = packageJson.main || 'index';
|
||||
var main;
|
||||
|
||||
// We prioritize the `browser` field if it's a module path.
|
||||
if (typeof packageJson.browser === 'string') {
|
||||
main = packageJson.browser;
|
||||
} else {
|
||||
main = packageJson.main || 'index';
|
||||
}
|
||||
|
||||
// If there is a mapping for main in the `browser` field.
|
||||
if (packageJson.browser && typeof packageJson.browser === 'object') {
|
||||
var tmpMain = packageJson.browser[main] ||
|
||||
packageJson.browser[withExtJs(main)] ||
|
||||
packageJson.browser[sansExtJs(main)];
|
||||
if (tmpMain) {
|
||||
main = tmpMain;
|
||||
}
|
||||
}
|
||||
|
||||
modulePath = withExtJs(path.join(packageJson._root, main));
|
||||
dep = this._graph[modulePath];
|
||||
|
||||
|
@ -207,8 +232,7 @@ DependecyGraph.prototype.resolveDependency = function(
|
|||
return dep;
|
||||
} else {
|
||||
|
||||
// 4. `depModuleId` is a module defined in a package relative to
|
||||
// `fromModule`.
|
||||
// `depModuleId` is a module defined in a package relative to `fromModule`.
|
||||
packageJson = this._lookupPackage(fromModule.path);
|
||||
|
||||
if (packageJson == null) {
|
||||
|
@ -224,12 +248,23 @@ DependecyGraph.prototype.resolveDependency = function(
|
|||
var dir = path.dirname(fromModule.path);
|
||||
modulePath = path.join(dir, depModuleId);
|
||||
|
||||
if (packageJson.browser && typeof packageJson.browser === 'object') {
|
||||
var relPath = './' + path.relative(packageJson._root, modulePath);
|
||||
var tmpModulePath = packageJson.browser[withExtJs(relPath)] ||
|
||||
packageJson.browser[sansExtJs(relPath)];
|
||||
if (tmpModulePath) {
|
||||
modulePath = path.join(packageJson._root, tmpModulePath);
|
||||
}
|
||||
}
|
||||
|
||||
// JS modules can be required without extensios.
|
||||
if (this._assetExts.indexOf(extname(modulePath)) === -1) {
|
||||
modulePath = withExtJs(modulePath);
|
||||
}
|
||||
|
||||
dep = this._graph[modulePath];
|
||||
|
||||
// Maybe the dependency is a directory and there is an index.js inside it.
|
||||
if (dep == null) {
|
||||
modulePath = path.join(dir, depModuleId, 'index.js');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue