diff --git a/.flowconfig b/.flowconfig index 56d38f308..31a4d5317 100644 --- a/.flowconfig +++ b/.flowconfig @@ -40,9 +40,9 @@ suppress_type=$FlowIssue suppress_type=$FlowFixMe suppress_type=$FixMe -suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-2]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) -suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-2]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-3]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy [version] -0.12.0 +0.13.1 diff --git a/Examples/UIExplorer/ActionSheetIOSExample.js b/Examples/UIExplorer/ActionSheetIOSExample.js index 86a3b96db..5c6ba8f96 100644 --- a/Examples/UIExplorer/ActionSheetIOSExample.js +++ b/Examples/UIExplorer/ActionSheetIOSExample.js @@ -17,11 +17,12 @@ var React = require('react-native'); var { + ActionSheetIOS, StyleSheet, Text, View, } = React; -var ActionSheetIOS = require('ActionSheetIOS'); + var BUTTONS = [ 'Button Index: 0', 'Button Index: 1', diff --git a/Examples/UIExplorer/AdSupportIOSExample.js b/Examples/UIExplorer/AdSupportIOSExample.js index ef7f076bf..1626249e7 100644 --- a/Examples/UIExplorer/AdSupportIOSExample.js +++ b/Examples/UIExplorer/AdSupportIOSExample.js @@ -15,10 +15,9 @@ */ 'use strict'; -var AdSupportIOS = require('AdSupportIOS'); - var React = require('react-native'); var { + AdSupportIOS, StyleSheet, Text, View, diff --git a/Examples/UIExplorer/MapViewExample.js b/Examples/UIExplorer/MapViewExample.js index 0d1061ace..572017574 100644 --- a/Examples/UIExplorer/MapViewExample.js +++ b/Examples/UIExplorer/MapViewExample.js @@ -16,9 +16,9 @@ 'use strict'; var React = require('react-native'); -var StyleSheet = require('StyleSheet'); var { MapView, + StyleSheet, Text, TextInput, View, diff --git a/Examples/UIExplorer/TransformExample.js b/Examples/UIExplorer/TransformExample.js index a59a019b3..9e848c3a5 100644 --- a/Examples/UIExplorer/TransformExample.js +++ b/Examples/UIExplorer/TransformExample.js @@ -6,12 +6,14 @@ 'use strict'; var React = require('React'); +var { + StyleSheet, + View, +} = React; -var StyleSheet = require('StyleSheet'); var TimerMixin = require('react-timer-mixin'); -var UIExplorerBlock = require('UIExplorerBlock'); -var UIExplorerPage = require('UIExplorerPage'); -var View = require('View'); +var UIExplorerBlock = require('./UIExplorerBlock'); +var UIExplorerPage = require('./UIExplorerPage'); var TransformExample = React.createClass({ diff --git a/Examples/UIExplorer/UIExplorerApp.android.js b/Examples/UIExplorer/UIExplorerApp.android.js index 69767c4b7..154796cef 100644 --- a/Examples/UIExplorer/UIExplorerApp.android.js +++ b/Examples/UIExplorer/UIExplorerApp.android.js @@ -17,14 +17,16 @@ 'use strict'; var React = require('react-native'); -var Dimensions = require('Dimensions'); -var DrawerLayoutAndroid = require('DrawerLayoutAndroid'); -var ToolbarAndroid = require('ToolbarAndroid'); -var UIExplorerList = require('./UIExplorerList'); var { + Dimensions, StyleSheet, View, } = React; +var UIExplorerList = require('./UIExplorerList'); + +// TODO: these should be exposed by the 'react-native' module. +var DrawerLayoutAndroid = require('DrawerLayoutAndroid'); +var ToolbarAndroid = require('ToolbarAndroid'); var DRAWER_WIDTH_LEFT = 56; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 5af5a32c6..1803c7ebd 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -21,6 +21,7 @@ var { ListView, PixelRatio, Platform, + Settings, StyleSheet, Text, TextInput, @@ -29,13 +30,20 @@ var { } = React; var { TestModule } = React.addons; -var Settings = require('Settings'); import type { ExampleModule } from 'ExampleTypes'; var createExamplePage = require('./createExamplePage'); var COMMON_COMPONENTS = [ + require('./ImageExample'), + require('./ListViewExample'), + require('./ListViewPagingExample'), + require('./MapViewExample'), + require('./Navigator/NavigatorExample'), + require('./ScrollViewExample'), + require('./TextInputExample'), + require('./TouchableExample'), require('./ViewExample'), require('./WebViewExample'), ]; @@ -51,23 +59,15 @@ if (Platform.OS === 'ios') { var COMPONENTS = COMMON_COMPONENTS.concat([ require('./ActivityIndicatorIOSExample'), require('./DatePickerIOSExample'), - require('./ImageExample'), - require('./ListViewExample'), - require('./ListViewPagingExample'), - require('./MapViewExample'), - require('./Navigator/NavigatorExample'), require('./NavigatorIOSColorsExample'), require('./NavigatorIOSExample'), require('./PickerIOSExample'), require('./ProgressViewIOSExample'), - require('./ScrollViewExample'), require('./SegmentedControlIOSExample'), require('./SliderIOSExample'), require('./SwitchIOSExample'), require('./TabBarIOSExample'), require('./TextExample.ios'), - require('./TextInputExample'), - require('./TouchableExample'), ]); var APIS = COMMON_APIS.concat([ diff --git a/Examples/UIExplorer/WebViewExample.js b/Examples/UIExplorer/WebViewExample.js index fe3cbef6f..b9137e87c 100644 --- a/Examples/UIExplorer/WebViewExample.js +++ b/Examples/UIExplorer/WebViewExample.js @@ -16,7 +16,6 @@ 'use strict'; var React = require('react-native'); -var StyleSheet = require('StyleSheet'); var { StyleSheet, Text, diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index 30d05210f..9aad783b0 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -20,6 +20,12 @@ var Touchable = require('Touchable'); var merge = require('merge'); var onlyChild = require('onlyChild'); +var invariant = require('invariant'); +invariant( + AnimationExperimental || POPAnimation, + 'Please add the RCTAnimationExperimental framework to your project, or add //Libraries/FBReactKit:RCTPOPAnimation to your BUCK file if running internally within Facebook.' +); + type State = { animationID: ?number; }; diff --git a/Libraries/Image/RCTImageDownloader.h b/Libraries/Image/RCTImageDownloader.h index 89a77975f..5a4dd1987 100644 --- a/Libraries/Image/RCTImageDownloader.h +++ b/Libraries/Image/RCTImageDownloader.h @@ -14,7 +14,7 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error); @interface RCTImageDownloader : NSObject -+ (instancetype)sharedInstance; ++ (RCTImageDownloader *)sharedInstance; /** * Downloads a block of raw data and returns it. Note that the callback block diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index 760fce614..a8a68c0b4 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -9,25 +9,27 @@ #import "RCTImageDownloader.h" -#import "RCTCache.h" #import "RCTLog.h" #import "RCTUtils.h" typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *error); +CGSize RCTTargetSizeForClipRect(CGRect); +CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); + @implementation RCTImageDownloader { - RCTCache *_cache; + NSURLCache *_cache; dispatch_queue_t _processingQueue; NSMutableDictionary *_pendingBlocks; } -+ (instancetype)sharedInstance ++ (RCTImageDownloader *)sharedInstance { static RCTImageDownloader *sharedInstance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - sharedInstance = [[self alloc] init]; + sharedInstance = [[RCTImageDownloader alloc] init]; }); return sharedInstance; } @@ -35,27 +37,22 @@ typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *e - (instancetype)init { if ((self = [super init])) { - _cache = [[RCTCache alloc] initWithName:@"RCTImageDownloader"]; + _cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 diskCapacity:200 * 1024 * 1024 diskPath:@"React/RCTImageDownloader"]; _processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL); _pendingBlocks = [[NSMutableDictionary alloc] init]; } - return self; -} -static NSString *RCTCacheKeyForURL(NSURL *url) -{ - return url.absoluteString; + return self; } - (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block { - NSString *cacheKey = RCTCacheKeyForURL(url); + NSString *cacheKey = url.absoluteString; __block BOOL cancelled = NO; __block NSURLSessionDataTask *task = nil; dispatch_block_t cancel = ^{ - cancelled = YES; dispatch_async(_processingQueue, ^{ @@ -88,21 +85,28 @@ static NSString *RCTCacheKeyForURL(NSURL *url) }); }; - if ([_cache hasDataForKey:cacheKey]) { - [_cache fetchDataForKey:cacheKey completionHandler:^(NSData *data) { - if (!cancelled) { - runBlocks(YES, data, nil); - } - }]; - } else { - task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - if (!cancelled) { - runBlocks(NO, data, error); - } - }]; + task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { + if (!cancelled) { + runBlocks(NO, data, error); + } - [task resume]; - } + RCTImageDownloader *strongSelf = weakSelf; + NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed]; + [strongSelf->_cache storeCachedResponse:cachedResponse forDataTask:task]; + task = nil; + }]; + + [_cache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse *cachedResponse) { + if (cancelled) { + return; + } + + if (cachedResponse) { + runBlocks(YES, cachedResponse.data, nil); + } else { + [task resume]; + } + }]; } }); @@ -111,22 +115,78 @@ static NSString *RCTCacheKeyForURL(NSURL *url) - (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block { - NSString *cacheKey = RCTCacheKeyForURL(url); - __weak RCTImageDownloader *weakSelf = self; return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) { - if (!cached) { - RCTImageDownloader *strongSelf = weakSelf; - [strongSelf->_cache setData:data forKey:cacheKey]; - } block(data, error); }]; } +- (id)downloadImageForURL:(NSURL *)url + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + backgroundColor:(UIColor *)backgroundColor + block:(RCTImageDownloadBlock)block +{ + return [self downloadDataForURL:url block:^(NSData *data, NSError *error) { + if (!data || error) { + block(nil, error); + return; + } + + if (CGSizeEqualToSize(size, CGSizeZero)) { + // Target size wasn't available yet, so abort image drawing + block(nil, nil); + return; + } + + UIImage *image = [UIImage imageWithData:data scale:scale]; + if (image) { + + // Get scale and size + CGFloat destScale = scale ?: RCTScreenScale(); + CGRect imageRect = RCTClipRect(image.size, image.scale, size, destScale, resizeMode); + CGSize destSize = RCTTargetSizeForClipRect(imageRect); + + // Opacity optimizations + UIColor *blendColor = nil; + BOOL opaque = !RCTImageHasAlpha(image.CGImage); + if (!opaque && backgroundColor) { + CGFloat alpha; + [backgroundColor getRed:NULL green:NULL blue:NULL alpha:&alpha]; + if (alpha > 0.999) { // no benefit to blending if background is translucent + opaque = YES; + blendColor = backgroundColor; + } + } + + // Decompress image at required size + UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale); + if (blendColor) { + [blendColor setFill]; + UIRectFill((CGRect){CGPointZero, destSize}); + } + [image drawInRect:imageRect]; + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + } + + block(image, nil); + }]; +} + +- (void)cancelDownload:(id)downloadToken +{ + if (downloadToken) { + ((dispatch_block_t)downloadToken)(); + } +} + +@end + /** * Returns the optimal context size for an image drawn using the clip rect * returned by RCTClipRect. */ -CGSize RCTTargetSizeForClipRect(CGRect); CGSize RCTTargetSizeForClipRect(CGRect clipRect) { return (CGSize){ @@ -141,7 +201,6 @@ CGSize RCTTargetSizeForClipRect(CGRect clipRect) * then calculates the optimal rectangle to draw the image into so that it will * be sized and positioned correctly if drawn using the specified content mode. */ -CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale, CGSize destSize, CGFloat destScale, UIViewContentMode resizeMode) @@ -202,66 +261,3 @@ CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale, return (CGRect){CGPointZero, destSize}; } } - -- (id)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - backgroundColor:(UIColor *)backgroundColor - block:(RCTImageDownloadBlock)block -{ - return [self downloadDataForURL:url block:^(NSData *data, NSError *error) { - - if (!data || error) { - block(nil, error); - return; - } - - if (CGSizeEqualToSize(size, CGSizeZero)) { - // Target size wasn't available yet, so abort image drawing - block(nil, nil); - return; - } - - UIImage *image = [UIImage imageWithData:data scale:scale]; - if (image) { - - // Get scale and size - CGFloat destScale = scale ?: RCTScreenScale(); - CGRect imageRect = RCTClipRect(image.size, image.scale, size, destScale, resizeMode); - CGSize destSize = RCTTargetSizeForClipRect(imageRect); - - // Opacity optimizations - UIColor *blendColor = nil; - BOOL opaque = !RCTImageHasAlpha(image.CGImage); - if (!opaque && backgroundColor) { - CGFloat alpha; - [backgroundColor getRed:NULL green:NULL blue:NULL alpha:&alpha]; - if (alpha > 0.999) { // no benefit to blending if background is translucent - opaque = YES; - blendColor = backgroundColor; - } - } - - // Decompress image at required size - UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale); - if (blendColor) { - [blendColor setFill]; - UIRectFill((CGRect){CGPointZero, destSize}); - } - [image drawInRect:imageRect]; - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } - block(image, nil); - }]; -} - -- (void)cancelDownload:(id)downloadToken -{ - if (downloadToken) { - ((dispatch_block_t)downloadToken)(); - } -} - -@end diff --git a/Libraries/Interaction/InteractionManager.js b/Libraries/Interaction/InteractionManager.js index 098dfcee9..37625978a 100644 --- a/Libraries/Interaction/InteractionManager.js +++ b/Libraries/Interaction/InteractionManager.js @@ -21,11 +21,6 @@ var setImmediate = require('setImmediate'); type Handle = number; -/** - * Maximum time a handle can be open before warning in DEV. - */ -var DEV_TIMEOUT = 2000; - var _emitter = new EventEmitter(); var _interactionSet = new Set(); var _addInteractionSet = new Set(); @@ -94,14 +89,6 @@ var InteractionManager = { scheduleUpdate(); var handle = ++_inc; _addInteractionSet.add(handle); - if (__DEV__) { - // Capture the stack trace of what created the handle. - var error = new Error( - 'InteractionManager: interaction handle not cleared within ' + - DEV_TIMEOUT + ' ms.' - ); - setDevTimeoutHandle(handle, error, DEV_TIMEOUT); - } return handle; }, @@ -166,19 +153,4 @@ function processUpdate() { _deleteInteractionSet.clear(); } -/** - * Wait until `timeout` has passed and warn if the handle has not been cleared. - */ -function setDevTimeoutHandle( - handle: Handle, - error: Error, - timeout: number -): void { - setTimeout(() => { - if (_interactionSet.has(handle)) { - console.warn(error.message + '\n' + error.stack); - } - }, timeout); -} - module.exports = InteractionManager; diff --git a/Libraries/RCTTest/RCTTestModule.m b/Libraries/RCTTest/RCTTestModule.m index 54c44513e..9ef60c611 100644 --- a/Libraries/RCTTest/RCTTestModule.m +++ b/Libraries/RCTTest/RCTTestModule.m @@ -55,7 +55,7 @@ RCT_EXPORT_METHOD(verifySnapshot:(RCTResponseSenderBlock)callback) }]; } -RCT_EXPORT_METHOD(sendAppEvent:(NSString *)name body:(id)body) +RCT_EXPORT_METHOD(sendAppEvent:(NSString *)name body:(nullable id)body) { [_bridge.eventDispatcher sendAppEventWithName:name body:body]; } diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index 367276567..42cf30f51 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -42,11 +42,14 @@ var ReactNative = Object.assign(Object.create(require('React')), { WebView: require('WebView'), // APIs + ActionSheetIOS: require('ActionSheetIOS'), + AdSupportIOS: require('AdSupportIOS'), AlertIOS: require('AlertIOS'), AppRegistry: require('AppRegistry'), AppStateIOS: require('AppStateIOS'), AsyncStorage: require('AsyncStorage'), CameraRoll: require('CameraRoll'), + Dimensions: require('Dimensions'), ImagePickerIOS: require('ImagePickerIOS'), InteractionManager: require('InteractionManager'), LayoutAnimation: require('LayoutAnimation'), @@ -55,6 +58,7 @@ var ReactNative = Object.assign(Object.create(require('React')), { PanResponder: require('PanResponder'), PixelRatio: require('PixelRatio'), PushNotificationIOS: require('PushNotificationIOS'), + Settings: require('Settings'), StatusBarIOS: require('StatusBarIOS'), StyleSheet: require('StyleSheet'), VibrationIOS: require('VibrationIOS'), diff --git a/React.podspec b/React.podspec index c7bfbbd7c..8c4992817 100644 --- a/React.podspec +++ b/React.podspec @@ -22,7 +22,7 @@ Pod::Spec.new do |s| s.default_subspec = 'Core' s.requires_arc = true s.platform = :ios, "7.0" - s.prepare_command = 'npm install' + s.prepare_command = 'npm install --production' s.preserve_paths = "cli.js", "Libraries/**/*.js", "lint", "linter.js", "node_modules", "package.json", "packager", "PATENTS", "react-native-cli" s.header_mappings_dir = "." diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 838e8bc80..b1bb6cf1a 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -219,6 +219,9 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL [_frameUpdateObservers addObject:moduleData]; } } + + [[NSNotificationCenter defaultCenter] postNotificationName:RCTDidCreateNativeModules + object:self]; } - (void)initJS @@ -721,7 +724,7 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil); dispatch_async(dispatch_get_main_queue(), ^{ - [self.perfStats.jsGraph tick:displayLink.timestamp]; + [self.perfStats.jsGraph onTick:displayLink.timestamp]; }); } @@ -731,7 +734,7 @@ RCT_NOT_IMPLEMENTED(-initWithBundleURL:(__unused NSURL *)bundleURL RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g"); - [self.perfStats.uiGraph tick:displayLink.timestamp]; + [self.perfStats.uiGraph onTick:displayLink.timestamp]; } - (void)startProfiling diff --git a/React/Base/RCTBridge.h b/React/Base/RCTBridge.h index 1d70a6367..f12f26158 100644 --- a/React/Base/RCTBridge.h +++ b/React/Base/RCTBridge.h @@ -33,6 +33,11 @@ RCT_EXTERN NSString *const RCTJavaScriptDidLoadNotification; */ RCT_EXTERN NSString *const RCTJavaScriptDidFailToLoadNotification; +/** + * This notification fires when the bridge created all registered native modules + */ +RCT_EXTERN NSString *const RCTDidCreateNativeModules; + /** * This block can be used to instantiate modules that require additional * init parameters, or additional configuration prior to being used. diff --git a/React/Base/RCTBridge.m b/React/Base/RCTBridge.m index 28fcc460f..c6baf82a6 100644 --- a/React/Base/RCTBridge.m +++ b/React/Base/RCTBridge.m @@ -20,6 +20,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification"; NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification"; NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification"; +NSString *const RCTDidCreateNativeModules = @"RCTDidCreateNativeModules"; @class RCTBatchedBridge; diff --git a/React/Base/RCTCache.h b/React/Base/RCTCache.h deleted file mode 100644 index 8704ff3db..000000000 --- a/React/Base/RCTCache.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import - -@interface RCTCache : NSObject - -- (instancetype)init; // name = @"default" -- (instancetype)initWithName:(NSString *)name; - -@property (nonatomic, assign) NSUInteger maximumDiskSize; // in bytes - -#pragma mark - Retrieval - -- (BOOL)hasDataForKey:(NSString *)key; -- (void)fetchDataForKey:(NSString *)key completionHandler:(void (^)(NSData *data))completionHandler; - -#pragma mark - Insertion - -- (void)setData:(NSData *)data forKey:(NSString *)key; -- (void)removeAllData; - -@end diff --git a/React/Base/RCTCache.m b/React/Base/RCTCache.m deleted file mode 100644 index 9390069c4..000000000 --- a/React/Base/RCTCache.m +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ - -#import "RCTCache.h" - -#import -#import - -#import "RCTAssert.h" - -static NSString *const RCTCacheSubdirectoryName = @"React"; -static NSString *const RCTKeyExtendedAttributeName = @"com.facebook.React.RCTCacheManager.Key"; -static NSMapTable *RCTLivingCachesByName; - -static NSError *RCTPOSIXError(int errorNumber) -{ - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @(strerror(errorNumber)) - }; - return [NSError errorWithDomain:NSPOSIXErrorDomain code:errorNumber userInfo:userInfo]; -} - -static NSString *RCTGetExtendedAttribute(NSURL *fileURL, NSString *key, NSError **error) -{ - const char *path = fileURL.fileSystemRepresentation; - ssize_t length = getxattr(path, key.UTF8String, NULL, 0, 0, 0); - if (length <= 0) { - if (error) *error = RCTPOSIXError(errno); - return nil; - } - - char *buffer = malloc(length); - length = getxattr(path, key.UTF8String, buffer, length, 0, 0); - if (length > 0) { - return [[NSString alloc] initWithBytesNoCopy:buffer length:length encoding:NSUTF8StringEncoding freeWhenDone:YES]; - } - - free(buffer); - if (error) *error = RCTPOSIXError(errno); - return nil; -} - -static BOOL RCTSetExtendedAttribute(NSURL *fileURL, NSString *key, NSString *value, NSError **error) -{ - const char *path = fileURL.fileSystemRepresentation; - - int result; - if (value) { - const char *valueUTF8String = value.UTF8String; - result = setxattr(path, key.UTF8String, valueUTF8String, strlen(valueUTF8String), 0, 0); - } else { - result = removexattr(path, key.UTF8String, 0); - } - - if (result) { - if (error) *error = RCTPOSIXError(errno); - return NO; - } - - return YES; -} - -#pragma mark - Cache Record - - -@interface RCTCacheRecord : NSObject - -@property (readonly) NSUUID *UUID; -@property (readonly, weak) dispatch_queue_t queue; -@property (nonatomic, copy) NSData *data; - -@end - -@implementation RCTCacheRecord - -- (instancetype)initWithUUID:(NSUUID *)UUID -{ - if ((self = [super init])) { - _UUID = [UUID copy]; - } - return self; -} - -- (void)enqueueBlock:(dispatch_block_t)block -{ - dispatch_queue_t queue = _queue; - if (!queue) { - NSString *queueName = [NSString stringWithFormat:@"com.facebook.React.RCTCache.%@", _UUID.UUIDString]; - queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL); - _queue = queue; - } - - dispatch_async(queue, block); -} - -@end - -#pragma mark - Cache - -@implementation RCTCache -{ - NSString *_name; - NSFileManager *_fileManager; - NSMutableDictionary *_storage; - NSURL *_cacheDirectoryURL; -} - -+ (void)initialize -{ - if (self == [RCTCache class]) { - RCTLivingCachesByName = [NSMapTable strongToWeakObjectsMapTable]; - } -} - -- (instancetype)init -{ - return [self initWithName:@"default"]; -} - -- (instancetype)initWithName:(NSString *)name -{ - RCTAssertParam(name); - RCTAssert(name.length < NAME_MAX, @"Name must be fewer than %i characters in length.", NAME_MAX); - RCTCache *cachedCache = [RCTLivingCachesByName objectForKey:name]; - if (cachedCache) { - self = cachedCache; - return self; - } - - if ((self = [super init])) { - _name = [name copy]; - _fileManager = [[NSFileManager alloc] init]; - _storage = [NSMutableDictionary dictionary]; - - NSURL *cacheDirectoryURL = [[_fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject]; - cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:RCTCacheSubdirectoryName isDirectory:YES]; - _cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:name isDirectory:YES]; - [_fileManager createDirectoryAtURL:_cacheDirectoryURL withIntermediateDirectories:YES attributes:nil error:NULL]; - - NSArray *fileURLs = [_fileManager contentsOfDirectoryAtURL:_cacheDirectoryURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:NULL]; - for (NSURL *fileURL in fileURLs) { - NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:fileURL.lastPathComponent]; - if (!UUID) continue; - - NSString *key = RCTGetExtendedAttribute(fileURL, RCTKeyExtendedAttributeName, NULL); - if (!key) { - [_fileManager removeItemAtURL:fileURL error:NULL]; - continue; - } - - _storage[key] = [[RCTCacheRecord alloc] initWithUUID:UUID]; - } - } - return self; -} - -- (BOOL)hasDataForKey:(NSString *)key -{ - return _storage[key] != nil; -} - -- (void)fetchDataForKey:(NSString *)key completionHandler:(void (^)(NSData *))completionHandler -{ - NSParameterAssert(key.length > 0); - NSParameterAssert(completionHandler != nil); - RCTCacheRecord *record = _storage[key]; - if (!record) { - completionHandler(nil); - return; - } - - [record enqueueBlock:^{ - if (!record.data) { - record.data = [NSData dataWithContentsOfURL:[_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]]; - } - completionHandler(record.data); - }]; -} - -- (void)setData:(NSData *)data forKey:(NSString *)key -{ - NSParameterAssert(key.length > 0); - RCTCacheRecord *record = _storage[key]; - if (!record) { - if (!data) return; - - record = [[RCTCacheRecord alloc] initWithUUID:[NSUUID UUID]]; - _storage[key] = record; - } - - NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]; - - UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; - [record enqueueBlock:^{ - if (data) { - [data writeToURL:fileURL options:NSDataWritingAtomic error:NULL]; - RCTSetExtendedAttribute(fileURL, RCTKeyExtendedAttributeName, key, NULL); - } else { - [_fileManager removeItemAtURL:fileURL error:NULL]; - } - - if (identifier != UIBackgroundTaskInvalid) { - [[UIApplication sharedApplication] endBackgroundTask:identifier]; - } - }]; -} - -- (void)removeAllData -{ - UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; - dispatch_group_t group = dispatch_group_create(); - - for (RCTCacheRecord *record in _storage.allValues) { - NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]; - dispatch_group_async(group, record.queue, ^{ - [_fileManager removeItemAtURL:fileURL error:NULL]; - }); - } - - if (identifier != UIBackgroundTaskInvalid) { - dispatch_group_notify(group, dispatch_get_main_queue(), ^{ - [[UIApplication sharedApplication] endBackgroundTask:identifier]; - }); - } - - [_storage removeAllObjects]; -} - -@end diff --git a/React/Base/RCTFPSGraph.h b/React/Base/RCTFPSGraph.h index 905829aba..0c0e2664a 100644 --- a/React/Base/RCTFPSGraph.h +++ b/React/Base/RCTFPSGraph.h @@ -18,6 +18,6 @@ typedef NS_ENUM(NSUInteger, RCTFPSGraphPosition) { - (instancetype)initWithFrame:(CGRect)frame graphPosition:(RCTFPSGraphPosition)position name:(NSString *)name color:(UIColor *)color NS_DESIGNATED_INITIALIZER; -- (void)tick:(NSTimeInterval)timestamp; +- (void)onTick:(NSTimeInterval)timestamp; @end diff --git a/React/Base/RCTFPSGraph.m b/React/Base/RCTFPSGraph.m index 5e9b0d855..1aa5efd7d 100644 --- a/React/Base/RCTFPSGraph.m +++ b/React/Base/RCTFPSGraph.m @@ -93,7 +93,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder) return label; } -- (void)tick:(NSTimeInterval)timestamp +- (void)onTick:(NSTimeInterval)timestamp { _frameCount++; if (_prevTime == -1) { diff --git a/React/Base/RCTJavaScriptExecutor.h b/React/Base/RCTJavaScriptExecutor.h index 146247009..fa5afc5cb 100644 --- a/React/Base/RCTJavaScriptExecutor.h +++ b/React/Base/RCTJavaScriptExecutor.h @@ -67,16 +67,5 @@ typedef void (^RCTJavaScriptCallback)(id json, NSError *error); @end -static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID"; -__used static void RCTSetExecutorID(id executor) -{ - static NSUInteger executorID = 0; - if (executor) { - objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN); - } -} - -__used static NSNumber *RCTGetExecutorID(id executor) -{ - return executor ? objc_getAssociatedObject(executor, RCTJavaScriptExecutorID) : @0; -} +void RCTSetExecutorID(id executor); +NSNumber *RCTGetExecutorID(id executor); diff --git a/React/Base/RCTJavaScriptExecutor.m b/React/Base/RCTJavaScriptExecutor.m new file mode 100644 index 000000000..04e3db433 --- /dev/null +++ b/React/Base/RCTJavaScriptExecutor.m @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTJavaScriptExecutor.h" + + +static const char *RCTJavaScriptExecutorID = "RCTJavaScriptExecutorID"; + +void RCTSetExecutorID(id executor) +{ + static NSUInteger executorID = 0; + if (executor) { + objc_setAssociatedObject(executor, RCTJavaScriptExecutorID, @(++executorID), OBJC_ASSOCIATION_RETAIN); + } +} + +NSNumber *RCTGetExecutorID(id executor) +{ + return executor ? objc_getAssociatedObject(executor, RCTJavaScriptExecutorID) : @0; +} diff --git a/React/Base/RCTModuleMethod.m b/React/Base/RCTModuleMethod.m index aca8267b9..f294085e2 100644 --- a/React/Base/RCTModuleMethod.m +++ b/React/Base/RCTModuleMethod.m @@ -40,10 +40,12 @@ static NSRegularExpression *typeRegex; static NSRegularExpression *selectorRegex; if (!typeRegex) { - NSString *unusedPattern = @"(?:(?:__unused|__attribute__\\(\\(unused\\)\\)))"; + NSString *unusedPattern = @"(?:__unused|__attribute__\\(\\(unused\\)\\))"; NSString *constPattern = @"(?:const)"; - NSString *constUnusedPattern = [NSString stringWithFormat:@"(?:(?:%@|%@)\\s*)", unusedPattern, constPattern]; - NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", constUnusedPattern]; + NSString *nullabilityPattern = @"(?:__nullable|__nonnull|nullable|nonnull)"; + NSString *annotationPattern = [NSString stringWithFormat:@"(?:(?:%@|%@|%@)\\s*)", + unusedPattern, constPattern, nullabilityPattern]; + NSString *pattern = [NSString stringWithFormat:@"\\(%1$@?(\\w+?)(?:\\s*\\*)?%1$@?\\)", annotationPattern]; typeRegex = [[NSRegularExpression alloc] initWithPattern:pattern options:0 error:NULL]; selectorRegex = [[NSRegularExpression alloc] initWithPattern:@"(?<=:).*?(?=[a-zA-Z_]+:|$)" options:0 error:NULL]; diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 099c120a0..54261357d 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -65,8 +65,8 @@ 58114A501AAE93D500E7D092 /* RCTAsyncLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 58114A4E1AAE93D500E7D092 /* RCTAsyncLocalStorage.m */; }; 58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; }; 63F014C01B02080B003B75D2 /* RCTPointAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 63F014BF1B02080B003B75D2 /* RCTPointAnnotation.m */; }; + 783ABB351B38A9D3003FFD95 /* RCTJavaScriptExecutor.m in Sources */ = {isa = PBXBuildFile; fileRef = 783ABB341B38A9D3003FFD95 /* RCTJavaScriptExecutor.m */; }; 830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; }; - 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; }; 832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; }; 83CBBA511A601E3B00E9B192 /* RCTAssert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA4B1A601E3B00E9B192 /* RCTAssert.m */; }; 83CBBA521A601E3B00E9B192 /* RCTLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA4E1A601E3B00E9B192 /* RCTLog.m */; }; @@ -216,11 +216,10 @@ 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = ""; }; 63F014BE1B02080B003B75D2 /* RCTPointAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointAnnotation.h; sourceTree = ""; }; 63F014BF1B02080B003B75D2 /* RCTPointAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPointAnnotation.m; sourceTree = ""; }; + 783ABB341B38A9D3003FFD95 /* RCTJavaScriptExecutor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptExecutor.m; sourceTree = ""; }; 830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = ""; }; 830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = ""; }; 830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = ""; }; - 830BA4531A8E3BDA00D53203 /* RCTCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCache.h; sourceTree = ""; }; - 830BA4541A8E3BDA00D53203 /* RCTCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCache.m; sourceTree = ""; }; 83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSparseArray.h; sourceTree = ""; }; 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSparseArray.m; sourceTree = ""; }; 83CBBA2E1A601D0E00E9B192 /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -425,8 +424,6 @@ 83CBBA5E1A601EAA00E9B192 /* RCTBridge.h */, 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */, 830213F31A654E0800B993E6 /* RCTBridgeModule.h */, - 830BA4531A8E3BDA00D53203 /* RCTCache.h */, - 830BA4541A8E3BDA00D53203 /* RCTCache.m */, 83CBBACA1A6023D300E9B192 /* RCTConvert.h */, 83CBBACB1A6023D300E9B192 /* RCTConvert.m */, 13AF1F851AE6E777005F5298 /* RCTDefines.h */, @@ -434,6 +431,7 @@ 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */, 83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */, 83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */, + 783ABB341B38A9D3003FFD95 /* RCTJavaScriptExecutor.m */, 13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */, 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */, 83CBBA4D1A601E3B00E9B192 /* RCTLog.h */, @@ -579,6 +577,7 @@ 134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */, 14C2CA781B3ACB0400E6CBB2 /* RCTBatchedBridge.m in Sources */, 13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */, + 783ABB351B38A9D3003FFD95 /* RCTJavaScriptExecutor.m in Sources */, 14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */, 14C2CA741B3AC64300E6CBB2 /* RCTModuleData.m in Sources */, 142014191B32094000CC17BA /* RCTPerformanceLogger.m in Sources */, @@ -601,7 +600,6 @@ 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */, 58114A171AAE854800E7D092 /* RCTPickerManager.m in Sources */, 13B0801A1A69489C00A75B9A /* RCTNavigator.m in Sources */, - 830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */, 137327E71AA5CF210034F82E /* RCTTabBar.m in Sources */, 00C1A2B31AC0B7E000E89A1C /* RCTDevMenu.m in Sources */, 63F014C01B02080B003B75D2 /* RCTPointAnnotation.m in Sources */, diff --git a/package.json b/package.json index 13c57fbeb..e4013c32a 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "yargs": "1.3.2" }, "devDependencies": { - "jest-cli": "0.4.5", + "jest-cli": "git://github.com/facebook/jest#0.5.x", "babel-eslint": "3.1.5", "eslint": "0.21.2", "eslint-plugin-react": "2.3.0" diff --git a/packager/blacklist.js b/packager/blacklist.js index a2ba71673..237691a85 100644 --- a/packager/blacklist.js +++ b/packager/blacklist.js @@ -8,6 +8,8 @@ */ 'use strict'; +var path = require('path'); + // Don't forget to everything listed here to `testConfig.json` // modulePathIgnorePatterns. var sharedBlacklist = [ @@ -24,7 +26,7 @@ var platformBlacklists = { ios: [ 'node_modules/react-tools/src/browser/ui/React.js', 'node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js', - // 'node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js', + 'node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js', '.web.js', '.android.js', ], @@ -32,14 +34,16 @@ var platformBlacklists = { 'node_modules/react-tools/src/browser/ui/React.js', 'node_modules/react-tools/src/browser/eventPlugins/ResponderEventPlugin.js', 'node_modules/react-tools/src/browser/ReactTextComponent.js', - // 'node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js', + 'node_modules/react-tools/src/vendor/core/ExecutionEnvironment.js', '.web.js', '.ios.js', ], }; function escapeRegExp(str) { - return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + var escaped = str.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + // convert the '/' into an escaped local file separator + return escaped.replace(/\//g,'\\' + path.sep); } function blacklist(platform, additionalBlacklist) { diff --git a/packager/package.json b/packager/package.json index f3af007f3..cc3f4fc6f 100644 --- a/packager/package.json +++ b/packager/package.json @@ -25,7 +25,7 @@ }, "dependencies": {}, "devDependencies": { - "jest-cli": "0.4.5", + "jest-cli": "git://github.com/facebook/jest#0.5.x", "eslint": "0.9.2" } } diff --git a/packager/packager.js b/packager/packager.js index f2d526e06..ff0faa315 100644 --- a/packager/packager.js +++ b/packager/packager.js @@ -12,6 +12,7 @@ var fs = require('fs'); var path = require('path'); var execFile = require('child_process').execFile; var http = require('http'); +var isAbsolutePath = require('absolute-path'); var getFlowTypeCheckMiddleware = require('./getFlowTypeCheckMiddleware'); @@ -56,6 +57,11 @@ var options = parseCommandLine([{ }, { command: 'nonPersistent', description: 'Disable file watcher' +}, { + command: 'transformer', + type: 'string', + default: require.resolve('./transformer.js'), + description: 'Specify a custom transformer to be used (absolute path)' }]); if (options.projectRoots) { @@ -63,8 +69,9 @@ if (options.projectRoots) { options.projectRoots = options.projectRoots.split(','); } } else { - if (__dirname.match(/node_modules\/react-native\/packager$/)) { - // packager is running from node_modules of another project + // match on either path separator + if (__dirname.match(/node_modules[\/\\]react-native[\/\\]packager$/)) { + // packager is running from node_modules of another project options.projectRoots = [path.resolve(__dirname, '../../..')]; } else if (__dirname.match(/Pods\/React\/packager$/)) { // packager is running from node_modules of another project @@ -91,7 +98,8 @@ if (options.assetRoots) { }); } } else { - if (__dirname.match(/node_modules\/react-native\/packager$/)) { + // match on either path separator + if (__dirname.match(/node_modules[\/\\]react-native[\/\\]packager$/)) { options.assetRoots = [path.resolve(__dirname, '../../..')]; } else if (__dirname.match(/Pods\/React\/packager$/)) { options.assetRoots = [path.resolve(__dirname, '../../..')]; @@ -208,12 +216,17 @@ function statusPageMiddleware(req, res, next) { } function getAppMiddleware(options) { + var transformerPath = options.transformer; + if (!isAbsolutePath(transformerPath)) { + transformerPath = path.resolve(process.cwd(), transformerPath); + } + return ReactPackager.middleware({ nonPersistent: options.nonPersistent, projectRoots: options.projectRoots, blacklistRE: blacklist(options.platform), cacheVersion: '2', - transformModulePath: require.resolve('./transformer.js'), + transformModulePath: transformerPath, assetRoots: options.assetRoots, assetExts: ['png', 'jpeg', 'jpg'], polyfillModuleNames: [ diff --git a/packager/react-packager/index.js b/packager/react-packager/index.js index d4ea0dd39..c47d762a1 100644 --- a/packager/react-packager/index.js +++ b/packager/react-packager/index.js @@ -8,7 +8,7 @@ */ 'use strict'; -require('babel/register')({ +require('babel-core/register')({ only: /react-packager\/src/ }); diff --git a/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js index 673d9c58a..471e60691 100644 --- a/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js @@ -2353,7 +2353,8 @@ describe('DependencyGraph', function() { } callbacks.push(callback); return this; - } + }, + isWatchman: () => Promise.resolve(false), }; }); diff --git a/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js index 8145bfa03..42c1a485f 100644 --- a/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js @@ -29,6 +29,7 @@ const validateOpts = declareOpts({ }, ignoreFilePath: { type: 'function', + default: function(){} }, fileWatcher: { diff --git a/packager/react-packager/src/DependencyResolver/crawlers/index.js b/packager/react-packager/src/DependencyResolver/crawlers/index.js index 71290af44..fe755bcb6 100644 --- a/packager/react-packager/src/DependencyResolver/crawlers/index.js +++ b/packager/react-packager/src/DependencyResolver/crawlers/index.js @@ -1,21 +1,11 @@ 'use strict'; const nodeCrawl = require('./node'); -//const watchmanCrawl = require('./watchman'); +const watchmanCrawl = require('./watchman'); function crawl(roots, options) { - return nodeCrawl(roots, options); - - // Although, in theory, watchman should be much faster; - // there is currently a bottleneck somewhere in the - // encoding/decoding that is causing it to be slower - // than node crawling. However, this should be fixed soon. - // https://github.com/facebook/watchman/issues/113 - /* const {fileWatcher} = options; return fileWatcher.isWatchman().then(isWatchman => { - - console.log(isWatchman); if (!isWatchman) { return false; } @@ -30,7 +20,7 @@ function crawl(roots, options) { } return nodeCrawl(roots, options); - });*/ + }); } module.exports = crawl; diff --git a/packager/react-packager/src/DependencyResolver/crawlers/watchman.js b/packager/react-packager/src/DependencyResolver/crawlers/watchman.js index d6479a513..1871e3ead 100644 --- a/packager/react-packager/src/DependencyResolver/crawlers/watchman.js +++ b/packager/react-packager/src/DependencyResolver/crawlers/watchman.js @@ -36,7 +36,7 @@ function watchmanRecReadDir(roots, {ignore, fileWatcher, exts}) { } } - const cmd = Promise.promisify(watcher.client.command.bind(watcher.client)); + const cmd = Promise.denodeify(watcher.client.command.bind(watcher.client)); return cmd(['query', watchedRoot, { 'suffix': exts, 'expression': ['allof', ['type', 'f'], 'exists', dirExpr], diff --git a/packager/react-packager/src/FileWatcher/index.js b/packager/react-packager/src/FileWatcher/index.js index 46b4667e7..d9ed7f32f 100644 --- a/packager/react-packager/src/FileWatcher/index.js +++ b/packager/react-packager/src/FileWatcher/index.js @@ -12,9 +12,11 @@ const EventEmitter = require('events').EventEmitter; const sane = require('sane'); const Promise = require('promise'); const exec = require('child_process').exec; +const _ = require('underscore'); const MAX_WAIT_TIME = 25000; +// TODO(amasad): can we use watchman version command instead?r const detectingWatcherClass = new Promise(function(resolve) { exec('which watchman', function(err, out) { if (err || out.length === 0) { @@ -79,9 +81,11 @@ class FileWatcher extends EventEmitter { static createDummyWatcher() { const ev = new EventEmitter(); - ev.end = function() { - return Promise.resolve(); - }; + _.extend(ev, { + isWatchman: () => Promise.resolve(false), + end: () => Promise.resolve(), + }); + return ev; } } diff --git a/packager/transformer.js b/packager/transformer.js index 9096cc879..c50bdd314 100644 --- a/packager/transformer.js +++ b/packager/transformer.js @@ -10,7 +10,7 @@ */ 'use strict'; -var babel = require('babel'); +var babel = require('babel-core'); function transform(srcTxt, filename, options) { var result = babel.transform(srcTxt, {