From c88a1cd9b82a9421b83af2a53cf9e1d7bc848e45 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Wed, 18 Feb 2015 17:51:14 -0800 Subject: [PATCH] Updates from Wed Feb 18 - [reactnative] s/SpinnerIOS/ActivityIndicatorIOS/ | Dan Witte - [react-packager] Add a non-persistent mode for static builds | Amjad Masad - [React Native] Fix stored file rejection when initializing cache | Alex Akers - [React Native] Consolidate network requests in image downloader | Alex Akers - [React Native] Update RCTCache | Alex Akers - Converted all low-hanging RKBridgeModules in FBReactKit to RCTBridgeModules | Nick Lockwood --- Examples/Movies/SearchScreen.js | 6 +- ...Example.js => ActivityIndicatorExample.js} | 48 ++-- Examples/UIExplorer/UIExplorerList.js | 2 +- .../ActivityIndicatorIOS.ios.js} | 10 +- .../react-native/react-native-interface.js | 2 +- Libraries/react-native/react-native.js | 2 +- ReactKit/Base/RCTBridge.m | 70 +++--- ReactKit/Base/RCTCache.m | 231 ++++++++++-------- ReactKit/Base/RCTEventDispatcher.h | 23 +- ReactKit/Base/RCTEventDispatcher.m | 14 +- ReactKit/Base/RCTImageDownloader.m | 120 +++++---- ReactKit/Modules/RCTUIManager.m | 10 +- ReactKit/Views/RCTNavigator.m | 4 +- ReactKit/Views/RCTWrapperViewController.m | 2 +- packager/react-packager/index.js | 22 +- .../__tests__/HasteDependencyResolver-test.js | 47 +++- .../src/DependencyResolver/haste/index.js | 7 +- .../react-packager/src/FileWatcher/index.js | 8 + .../__tests__/Transformer-test.js | 8 +- .../react-packager/src/JSTransformer/index.js | 29 ++- packager/react-packager/src/Packager/index.js | 18 +- packager/react-packager/src/Server/index.js | 13 +- 22 files changed, 427 insertions(+), 269 deletions(-) rename Examples/UIExplorer/{SpinnerExample.js => ActivityIndicatorExample.js} (69%) rename Libraries/Components/{SpinnerIOS/SpinnerIOS.ios.js => ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js} (91%) diff --git a/Examples/Movies/SearchScreen.js b/Examples/Movies/SearchScreen.js index 06fed2e2c09ff6..2cd67aa192f930 100644 --- a/Examples/Movies/SearchScreen.js +++ b/Examples/Movies/SearchScreen.js @@ -9,7 +9,7 @@ var { ListView, ListViewDataSource, ScrollView, - SpinnerIOS, + ActivityIndicatorIOS, StyleSheet, Text, TextInput, @@ -215,7 +215,7 @@ var SearchScreen = React.createClass({ if (!this.hasMore() || !this.state.isLoadingTail) { return ; } - return ; + return ; }, renderRow: function(movie: Object) { @@ -290,7 +290,7 @@ var SearchBar = React.createClass({ onFocus={this.props.onFocus} style={styles.searchBarInput} /> - diff --git a/Examples/UIExplorer/SpinnerExample.js b/Examples/UIExplorer/ActivityIndicatorExample.js similarity index 69% rename from Examples/UIExplorer/SpinnerExample.js rename to Examples/UIExplorer/ActivityIndicatorExample.js index 3d6dcbe3279fff..4f068254deec1d 100644 --- a/Examples/UIExplorer/SpinnerExample.js +++ b/Examples/UIExplorer/ActivityIndicatorExample.js @@ -1,19 +1,19 @@ /** * Copyright 2004-present Facebook. All Rights Reserved. * - * @providesModule SpinnerExample + * @providesModule ActivityIndicatorExample */ 'use strict'; var React = require('react-native'); var { - SpinnerIOS, + ActivityIndicatorIOS, StyleSheet, TimerMixin, View, } = React; -var ToggleAnimatingSpinner = React.createClass({ +var ToggleAnimatingActivityIndicator = React.createClass({ mixins: [TimerMixin], getInitialState: function() { @@ -38,17 +38,17 @@ var ToggleAnimatingSpinner = React.createClass({ render: function() { return ( - ); } }); exports.framework = 'React'; -exports.title = ''; +exports.title = ''; exports.description = 'Animated loading indicators.'; exports.examples = [ @@ -56,7 +56,7 @@ exports.examples = [ title: 'Default (small, white)', render: function() { return ( - @@ -68,10 +68,10 @@ exports.examples = [ render: function() { return ( - - @@ -83,10 +83,10 @@ exports.examples = [ render: function() { return ( - - - - + + + + ); } @@ -95,10 +95,10 @@ exports.examples = [ title: 'Large', render: function() { return ( - ); } @@ -108,20 +108,20 @@ exports.examples = [ render: function() { return ( - - - - @@ -131,7 +131,7 @@ exports.examples = [ { title: 'Start/stop', render: function() { - return ; + return ; } }, ]; diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 581f34b36dbd33..56734c70fa7666 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -29,7 +29,7 @@ var EXAMPLES = [ require('./StatusBarIOSExample'), require('./PointerEventsExample'), require('./TouchableExample'), - require('./SpinnerExample'), + require('./ActivityIndicatorExample'), require('./ScrollViewExample'), ]; diff --git a/Libraries/Components/SpinnerIOS/SpinnerIOS.ios.js b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js similarity index 91% rename from Libraries/Components/SpinnerIOS/SpinnerIOS.ios.js rename to Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js index 6aadabad55761c..a281cf97aa929c 100644 --- a/Libraries/Components/SpinnerIOS/SpinnerIOS.ios.js +++ b/Libraries/Components/ActivityIndicatorIOS/ActivityIndicatorIOS.ios.js @@ -1,7 +1,7 @@ /** * Copyright 2004-present Facebook. All Rights Reserved. * - * @providesModule SpinnerIOS + * @providesModule ActivityIndicatorIOS */ 'use strict'; @@ -25,7 +25,7 @@ var SpinnerSize = keyMirror({ var GRAY = '#999999'; -var SpinnerIOS = React.createClass({ +var ActivityIndicatorIOS = React.createClass({ mixins: [NativeMethodsMixin], propTypes: { @@ -39,8 +39,8 @@ var SpinnerIOS = React.createClass({ color: PropTypes.string, /** * The size of the spinner, must be one of: - * - SpinnerIOS.size.large - * - SpinnerIOS.size.small (default) + * - ActivityIndicatorIOS.size.large + * - ActivityIndicatorIOS.size.small (default) */ size: PropTypes.oneOf([SpinnerSize.large, SpinnerSize.small]), }, @@ -101,4 +101,4 @@ var UIActivityIndicatorView = createReactIOSNativeComponentClass({ uiViewClassName: 'UIActivityIndicatorView', }); -module.exports = SpinnerIOS; +module.exports = ActivityIndicatorIOS; diff --git a/Libraries/react-native/react-native-interface.js b/Libraries/react-native/react-native-interface.js index 419da184da1c19..8a99d1188c96cd 100644 --- a/Libraries/react-native/react-native-interface.js +++ b/Libraries/react-native/react-native-interface.js @@ -11,7 +11,7 @@ declare module "react-native" { declare var NavigatorItemIOS: ReactClass; declare var PixelRatio: ReactClass; declare var ScrollView: ReactClass; - declare var SpinnerIOS: ReactClass; + declare var ActivityIndicatorIOS: ReactClass; declare var StyleSheet: ReactClass; declare var Text: ReactClass; declare var TextInput: ReactClass; diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index 7b4765c974228b..55b0d95d29223c 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -16,7 +16,7 @@ var ReactNative = { NavigatorIOS: require('NavigatorIOS'), PixelRatio: require('PixelRatio'), ScrollView: require('ScrollView'), - SpinnerIOS: require('SpinnerIOS'), + ActivityIndicatorIOS: require('ActivityIndicatorIOS'), StatusBarIOS: require('StatusBarIOS'), StyleSheet: require('StyleSheet'), Text: require('Text'), diff --git a/ReactKit/Base/RCTBridge.m b/ReactKit/Base/RCTBridge.m index 9bd0dae35bfad1..cdd6af842b9be4 100644 --- a/ReactKit/Base/RCTBridge.m +++ b/ReactKit/Base/RCTBridge.m @@ -235,8 +235,9 @@ - (NSString *)description RCTRemoteModulesByID = [[NSMutableDictionary alloc] init]; remoteModules = [[NSMutableDictionary alloc] init]; - [RCTExportedMethodsByModule() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, NSArray *rawMethods, BOOL *stop) { - + [RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) { + + NSArray *rawMethods = RCTExportedMethodsByModule()[moduleName]; NSMutableDictionary *methods = [NSMutableDictionary dictionaryWithCapacity:rawMethods.count]; [rawMethods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop) { methods[method.JSMethodName] = @{ @@ -249,13 +250,15 @@ - (NSString *)description @"moduleID": @(remoteModules.count), @"methods": methods }; - - Class cls = RCTBridgeModuleClasses()[moduleName]; - if (RCTClassOverridesClassMethod(cls, @selector(constantsToExport))) { - module = [module mutableCopy]; - ((NSMutableDictionary *)module)[@"constants"] = [cls constantsToExport]; + + if (RCTClassOverridesClassMethod(moduleClass, @selector(constantsToExport))) { + NSDictionary *constants = [moduleClass constantsToExport]; + if (constants.count) { + module = [module mutableCopy]; + ((NSMutableDictionary *)module)[@"constants"] = constants; + } } - remoteModules[moduleName] = module; + remoteModules[moduleName] = [module copy]; // Add module lookup RCTRemoteModulesByID[module[@"moduleID"]] = moduleName; @@ -303,13 +306,13 @@ - (NSString *)description // Add globally used methods [JSMethods addObjectsFromArray:@[ @"Bundler.runApplication", + @"RCTDeviceEventEmitter.emit", @"RCTEventEmitter.receiveEvent", @"RCTEventEmitter.receiveTouches", ]]; // NOTE: these methods are currently unused in the OSS project // @"Dimensions.set", - // @"RCTDeviceEventEmitter.emit", // @"RCTNativeAppEventEmitter.emit", // @"ReactIOS.unmountComponentAtNodeAndRemoveContainer", @@ -376,10 +379,15 @@ - (instancetype)initWithJavaScriptExecutor:(id)javaScript _moduleInstances = [[NSMutableDictionary alloc] init]; [RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) { if (_moduleInstances[moduleName] == nil) { + id moduleInstance; if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) { - _moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self]; + moduleInstance = [[moduleClass alloc] initWithBridge:self]; } else { - _moduleInstances[moduleName] = [[moduleClass alloc] init]; + moduleInstance = [[moduleClass alloc] init]; + } + if (moduleInstance) { + // If nil, the module doesn't support auto-instantiation + _moduleInstances[moduleName] = moduleInstance; } } }]; @@ -522,7 +530,7 @@ - (void)_handleBuffer:(id)buffer } if (![buffer isKindOfClass:[NSArray class]]) { - RCTLogMustFix(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class])); + RCTLogError(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class])); return; } @@ -530,14 +538,14 @@ - (void)_handleBuffer:(id)buffer NSUInteger bufferRowCount = [requestsArray count]; NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1; if (bufferRowCount != expectedFieldsCount) { - RCTLogMustFix(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount); + RCTLogError(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount); return; } for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) { id field = [requestsArray objectAtIndex:fieldIndex]; if (![field isKindOfClass:[NSArray class]]) { - RCTLogMustFix(@"Field at index %zd in buffer must be an instance of NSArray, got %@", fieldIndex, NSStringFromClass([field class])); + RCTLogError(@"Field at index %zd in buffer must be an instance of NSArray, got %@", fieldIndex, NSStringFromClass([field class])); return; } } @@ -549,7 +557,7 @@ - (void)_handleBuffer:(id)buffer NSUInteger numRequests = [moduleIDs count]; BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count]; if (!allSame) { - RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests); + RCTLogError(@"Invalid data message - all must be length: %zd", numRequests); return; } @@ -578,30 +586,30 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i params:(NSArray *)params { if (![params isKindOfClass:[NSArray class]]) { - RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i); + RCTLogError(@"Invalid module/method/params tuple for request #%zd", i); return NO; } NSString *moduleName = RCTRemoteModulesByID[moduleID]; if (!moduleName) { - RCTLogMustFix(@"Unknown moduleID: %@", moduleID); + RCTLogError(@"Unknown moduleID: %@", moduleID); return NO; } NSArray *methods = RCTExportedMethodsByModule()[moduleName]; if (methodID >= methods.count) { - RCTLogMustFix(@"Unknown methodID: %zd for module: %@", methodID, moduleName); + RCTLogError(@"Unknown methodID: %zd for module: %@", methodID, moduleName); return NO; } RCTModuleMethod *method = methods[methodID]; NSUInteger methodArity = method.arity; if (params.count != methodArity) { - RCTLogMustFix(@"Expected %tu arguments but got %tu invoking %@.%@", - methodArity, - params.count, - moduleName, - method.JSMethodName); + RCTLogError(@"Expected %tu arguments but got %tu invoking %@.%@", + methodArity, + params.count, + moduleName, + method.JSMethodName); return NO; } @@ -663,13 +671,13 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i // TODO: it seems like an error if the param doesn't respond // so we should probably surface that error rather than failing silently #define CASE(_value, _type, _selector) \ -case _value: \ -if ([param respondsToSelector:@selector(_selector)]) { \ -_type value = [param _selector]; \ -[invocation setArgument:&value atIndex:argIdx]; \ -shouldSet = NO; \ -} \ -break; + case _value: \ + if ([param respondsToSelector:@selector(_selector)]) { \ + _type value = [param _selector]; \ + [invocation setArgument:&value atIndex:argIdx]; \ + shouldSet = NO; \ + } \ + break; CASE('c', char, charValue) CASE('C', unsigned char, unsignedCharValue) @@ -698,7 +706,7 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i [invocation invoke]; } @catch (NSException *exception) { - RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception); + RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception); } }); diff --git a/ReactKit/Base/RCTCache.m b/ReactKit/Base/RCTCache.m index 40edaa557f816f..498db2cd05e301 100644 --- a/ReactKit/Base/RCTCache.m +++ b/ReactKit/Base/RCTCache.m @@ -5,21 +5,90 @@ #import #import -static NSString *const CacheSubdirectoryName = @"ReactKit"; -static NSString *const KeyExtendedAttributeName = @"com.facebook.ReactKit.RCTCacheManager.Key"; -static dispatch_queue_t Queue; +static NSString *const RCTCacheSubdirectoryName = @"ReactKit"; +static NSString *const RCTKeyExtendedAttributeName = @"com.facebook.ReactKit.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 (nonatomic, copy) NSUUID *UUID; +@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.ReactKit.RCTCache.%@", _UUID.UUIDString]; + queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL); + _queue = queue; + } + + dispatch_async(queue, block); +} + @end #pragma mark - Cache @@ -35,9 +104,10 @@ @implementation RCTCache + (void)initialize { if (self == [RCTCache class]) { - Queue = dispatch_queue_create("com.facebook.ReactKit.RCTCache", DISPATCH_QUEUE_SERIAL); + RCTLivingCachesByName = [NSMapTable strongToWeakObjectsMapTable]; } } + - (instancetype)init { return [self initWithName:@"default"]; @@ -46,46 +116,39 @@ - (instancetype)init - (instancetype)initWithName:(NSString *)name { NSParameterAssert(name.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:CacheSubdirectoryName isDirectory:YES]; + 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; + NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:fileURL.lastPathComponent]; + if (!UUID) continue; - NSString *key = [self keyOfItemAtURL:fileURL error:NULL]; + NSString *key = RCTGetExtendedAttribute(fileURL, RCTKeyExtendedAttributeName, NULL); if (!key) { [_fileManager removeItemAtURL:fileURL error:NULL]; continue; } - RCTCacheRecord *record = [[RCTCacheRecord alloc] init]; - record.UUID = uuid; - _storage[key] = record; + _storage[key] = [[RCTCacheRecord alloc] initWithUUID:UUID]; } } return self; } -- (void)runOnQueue:(dispatch_block_t)block -{ - UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; - dispatch_async(Queue, ^{ - if (block) block(); - if (identifier != UIBackgroundTaskInvalid) { - [[UIApplication sharedApplication] endBackgroundTask:identifier]; - } - }); -} - - (BOOL)hasDataForKey:(NSString *)key { return _storage[key] != nil; @@ -95,115 +158,67 @@ - (void)fetchDataForKey:(NSString *)key completionHandler:(void (^)(NSData *))co { NSParameterAssert(key.length > 0); NSParameterAssert(completionHandler != nil); - [self runOnQueue:^{ - RCTCacheRecord *record = _storage[key]; - if (record && !record.data) { + RCTCacheRecord *record = _storage[key]; + if (!record) { + completionHandler(nil); + return; + } + + [record enqueueBlock:^{ + if (!record.data) { record.data = [NSData dataWithContentsOfURL:[_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]]; } - - dispatch_async(dispatch_get_main_queue(), ^{ - completionHandler(record.data); - }); + completionHandler(record.data); }]; } - (void)setData:(NSData *)data forKey:(NSString *)key { NSParameterAssert(key.length > 0); - [self runOnQueue:^{ - RCTCacheRecord *record = _storage[key]; - if (data) { - if (!record) { - record = [[RCTCacheRecord alloc] init]; - record.UUID = [NSUUID UUID]; - _storage[key] = record; - } + RCTCacheRecord *record = _storage[key]; + if (!record) { + if (!data) return; - record.data = data; + record = [[RCTCacheRecord alloc] initWithUUID:[NSUUID UUID]]; + _storage[key] = record; + } - NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]; - [data writeToURL:fileURL options:NSDataWritingAtomic error:NULL]; - } else if (record) { - [_storage removeObjectForKey:key]; + NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]; - 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 { - [self runOnQueue:^{ - [_storage removeAllObjects]; + UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]; + dispatch_group_t group = dispatch_group_create(); - NSDirectoryEnumerator *enumerator = [_fileManager enumeratorAtURL:_cacheDirectoryURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:nil]; - for (NSURL *fileURL in enumerator) { + [_storage enumerateKeysAndObjectsUsingBlock:^(NSString *key, RCTCacheRecord *record, BOOL *stop) { + NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]; + dispatch_group_async(group, record.queue, ^{ [_fileManager removeItemAtURL:fileURL error:NULL]; - } + }); }]; -} - -#pragma mark - Extended Attributes - -- (NSError *)errorWithPOSIXErrorNumber:(int)errorNumber -{ - NSDictionary *userInfo = @{ - NSLocalizedDescriptionKey: @(strerror(errorNumber)) - }; - return [NSError errorWithDomain:NSPOSIXErrorDomain code:errorNumber userInfo:userInfo]; -} - -- (BOOL)setAttribute:(NSString *)key value:(NSString *)value ofItemAtURL:(NSURL *)fileURL error:(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 = [self errorWithPOSIXErrorNumber:errno]; - return NO; - } - - return YES; -} -- (NSString *)attribute:(NSString *)key ofItemAtURL:(NSURL *)fileURL error:(NSError **)error -{ - const char *path = fileURL.fileSystemRepresentation; - const ssize_t length = getxattr(path, key.UTF8String, NULL, 0, 0, 0); - if (length <= 0) { - if (error) *error = [self errorWithPOSIXErrorNumber:errno]; - return nil; - } - - char *buffer = malloc(length); - ssize_t result = getxattr(path, key.UTF8String, buffer, length, 0, 0); - if (result == 0) { - return [[NSString alloc] initWithBytesNoCopy:buffer length:length encoding:NSUTF8StringEncoding freeWhenDone:YES]; + if (identifier != UIBackgroundTaskInvalid) { + dispatch_group_notify(group, dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] endBackgroundTask:identifier]; + }); } - free(buffer); - if (error) *error = [self errorWithPOSIXErrorNumber:errno]; - return nil; -} - -#pragma mark - Extended Attributes - Key - -- (NSString *)keyOfItemAtURL:(NSURL *)fileURL error:(NSError **)error -{ - return [self attribute:KeyExtendedAttributeName ofItemAtURL:fileURL error:error]; -} - -- (BOOL)setKey:(NSString *)key ofItemAtURL:(NSURL *)fileURL error:(NSError **)error -{ - return [self setAttribute:KeyExtendedAttributeName value:key ofItemAtURL:fileURL error:error]; + [_storage removeAllObjects]; } @end diff --git a/ReactKit/Base/RCTEventDispatcher.h b/ReactKit/Base/RCTEventDispatcher.h index 4cfc0d94a99013..fba5e7668e0019 100644 --- a/ReactKit/Base/RCTEventDispatcher.h +++ b/ReactKit/Base/RCTEventDispatcher.h @@ -21,27 +21,36 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) { RCTScrollEventTypeEndAnimation, }; +/** + * This class wraps the -[RCTBridge enqueueJSCall:args:] method, and + * provides some convenience methods for generating event calls. + */ @interface RCTEventDispatcher : NSObject - (instancetype)initWithBridge:(RCTBridge *)bridge; /** - * Send a named event. For most purposes, use the an - * event type of RCTEventTypeDefault, the other types - * are used internally by the React framework. + * Send a device or application event that does not relate to a specific + * view, e.g. rotation, location, keyboard show/hide, background/awake, etc. + */ +- (void)sendDeviceEventWithName:(NSString *)name body:(NSDictionary *)body; + +/** + * Send a user input event. The body dictionary must contain a "target" + * parameter, representing the react tag of the view sending the event */ -- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body; +- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body; /** - * Send text events + * Send a text input/focus event. */ - (void)sendTextEventWithType:(RCTTextEventType)type reactTag:(NSNumber *)reactTag text:(NSString *)text; /** - * Send scroll events - * (You can send a fake scroll event by passing nil for scrollView) + * Send a scroll event. + * (You can send a fake scroll event by passing nil for scrollView). */ - (void)sendScrollEventWithType:(RCTScrollEventType)type reactTag:(NSNumber *)reactTag diff --git a/ReactKit/Base/RCTEventDispatcher.m b/ReactKit/Base/RCTEventDispatcher.m index 339873358e7246..2805cf6e5fb625 100644 --- a/ReactKit/Base/RCTEventDispatcher.m +++ b/ReactKit/Base/RCTEventDispatcher.m @@ -4,7 +4,6 @@ #import "RCTAssert.h" #import "RCTBridge.h" -#import "UIView+ReactKit.h" @implementation RCTEventDispatcher { @@ -19,7 +18,14 @@ - (instancetype)initWithBridge:(RCTBridge *)bridge return self; } -- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body +- (void)sendDeviceEventWithName:(NSString *)name body:(NSDictionary *)body +{ + [_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit" + args:body ? @[name, body] : @[name]]; +} + + +- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body { RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]], @"Event body dictionary must include a 'target' property containing a react tag"); @@ -40,7 +46,7 @@ - (void)sendTextEventWithType:(RCTTextEventType)type @"topEndEditing", }; - [self sendEventWithName:events[type] body:@{ + [self sendInputEventWithName:events[type] body:@{ @"text": text, @"target": reactTag }]; @@ -91,7 +97,7 @@ - (void)sendScrollEventWithType:(RCTScrollEventType)type body = mutableBody; } - [self sendEventWithName:events[type] body:body]; + [self sendInputEventWithName:events[type] body:body]; } @end diff --git a/ReactKit/Base/RCTImageDownloader.m b/ReactKit/Base/RCTImageDownloader.m index 8efbe3b50bfd65..9f065aaa33292f 100644 --- a/ReactKit/Base/RCTImageDownloader.m +++ b/ReactKit/Base/RCTImageDownloader.m @@ -7,9 +7,12 @@ // TODO: something a bit more sophisticated +typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *error); + @implementation RCTImageDownloader { RCTCache *_cache; + NSMutableDictionary *_pendingBlocks; } + (instancetype)sharedInstance @@ -27,6 +30,7 @@ - (instancetype)init { if ((self = [super init])) { _cache = [[RCTCache alloc] initWithName:@"RCTImageDownloader"]; + _pendingBlocks = [NSMutableDictionary dictionary]; } return self; } @@ -36,8 +40,7 @@ - (NSString *)cacheKeyForURL:(NSURL *)url return url.absoluteString; } -- (id)_downloadDataForURL:(NSURL *)url - block:(RCTDataDownloadBlock)block +- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block { NSString *cacheKey = [self cacheKeyForURL:url]; @@ -45,36 +48,58 @@ - (id)_downloadDataForURL:(NSURL *)url __block NSURLSessionDataTask *task = nil; dispatch_block_t cancel = ^{ cancelled = YES; + + NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey]; + [pendingBlocks removeObject:block]; + if (task) { [task cancel]; task = nil; } }; - if ([_cache hasDataForKey:cacheKey]) { - [_cache fetchDataForKey:cacheKey completionHandler:^(NSData *data) { - if (cancelled) return; - block(data, nil); - }]; + NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey]; + if (pendingBlocks) { + [pendingBlocks addObject:block]; } else { - task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { - block(data, error); - }]; + _pendingBlocks[cacheKey] = [NSMutableArray arrayWithObject:block]; + + __weak RCTImageDownloader *weakSelf = self; + RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSData *data, NSError *error) { + RCTImageDownloader *strongSelf = weakSelf; + NSArray *blocks = strongSelf->_pendingBlocks[cacheKey]; + [strongSelf->_pendingBlocks removeObjectForKey:cacheKey]; + + for (RCTCachedDataDownloadBlock block in blocks) { + block(cached, data, error); + } + }; - [task resume]; + 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 resume]; + } } return [cancel copy]; } -- (id)downloadDataForURL:(NSURL *)url - block:(RCTDataDownloadBlock)block +- (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block { NSString *cacheKey = [self cacheKeyForURL:url]; __weak RCTImageDownloader *weakSelf = self; - return [self _downloadDataForURL:url block:^(NSData *data, NSError *error) { - RCTImageDownloader *strongSelf = weakSelf; - [strongSelf->_cache setData:data forKey:cacheKey]; + return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) { + if (!cached) { + RCTImageDownloader *strongSelf = weakSelf; + [strongSelf->_cache setData:data forKey:cacheKey]; + } dispatch_async(dispatch_get_main_queue(), ^{ block(data, error); @@ -82,52 +107,51 @@ - (id)downloadDataForURL:(NSURL *)url }]; } -- (id)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - block:(RCTImageDownloadBlock)block +- (id)downloadImageForURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale block:(RCTImageDownloadBlock)block { NSString *cacheKey = [self cacheKeyForURL:url]; __weak RCTImageDownloader *weakSelf = self; - return [self _downloadDataForURL:url block:^(NSData *data, NSError *error) { - if (data) { - UIImage *image = [UIImage imageWithData:data scale:scale]; - - if (image) { - CGSize imageSize = size; - if (CGSizeEqualToSize(imageSize, CGSizeZero)) { - imageSize = image.size; - } - - CGFloat imageScale = scale; - if (imageScale == 0 || imageScale > image.scale) { - imageScale = image.scale; - } - - UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale); - [image drawInRect:(CGRect){{0, 0}, imageSize}]; - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); + return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) { + if (!data) { + return dispatch_async(dispatch_get_main_queue(), ^{ + block(nil, error); + }); + } + UIImage *image = [UIImage imageWithData:data scale:scale]; + + if (image) { + CGSize imageSize = size; + if (CGSizeEqualToSize(imageSize, CGSizeZero)) { + imageSize = image.size; + } + + CGFloat imageScale = scale; + if (imageScale == 0 || imageScale > image.scale) { + imageScale = image.scale; + } + + UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale); + [image drawInRect:(CGRect){{0, 0}, imageSize}]; + image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + if (!cached) { RCTImageDownloader *strongSelf = weakSelf; [strongSelf->_cache setData:UIImagePNGRepresentation(image) forKey:cacheKey]; } - - dispatch_async(dispatch_get_main_queue(), ^{ - block(image, nil); - }); - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - block(nil, error); - }); } + + dispatch_async(dispatch_get_main_queue(), ^{ + block(image, nil); + }); }]; } - (void)cancelDownload:(id)downloadToken { if (downloadToken) { - dispatch_block_t block = downloadToken; + dispatch_block_t block = (id)downloadToken; block(); } } diff --git a/ReactKit/Modules/RCTUIManager.m b/ReactKit/Modules/RCTUIManager.m index 27a23c43f0d33e..3b1b56432886e9 100644 --- a/ReactKit/Modules/RCTUIManager.m +++ b/ReactKit/Modules/RCTUIManager.m @@ -677,7 +677,10 @@ - (void)createAndRegisterViewWithReactTag:(NSNumber *)reactTag RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName); manager = [[RCTViewManager alloc] init]; } - + + // Register manager + _viewManagerRegistry[reactTag] = manager; + // Generate default view, used for resetting default props if (!_defaultShadowViews[moduleName]) { _defaultShadowViews[moduleName] = [manager shadowView]; @@ -691,10 +694,7 @@ - (void)createAndRegisterViewWithReactTag:(NSNumber *)reactTag [self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){ RCTCAssertMainThread(); - - // Register manager - uiManager->_viewManagerRegistry[reactTag] = manager; - + // Generate default view, used for resetting default props if (!uiManager->_defaultViews[moduleName]) { // Note the default is setup after the props are read for the first time ever diff --git a/ReactKit/Views/RCTNavigator.m b/ReactKit/Views/RCTNavigator.m index d03ff7874feda2..74e1621c25b1fd 100644 --- a/ReactKit/Views/RCTNavigator.m +++ b/ReactKit/Views/RCTNavigator.m @@ -324,7 +324,7 @@ - (void)reportNavigationProgress:(CADisplayLink *)sender return; } _mostRecentProgress = nextProgress; - [_eventDispatcher sendEventWithName:@"topNavigationProgress" body:@{ + [_eventDispatcher sendInputEventWithName:@"topNavigationProgress" body:@{ @"fromIndex": @(_currentlyTransitioningFrom), @"toIndex": @(_currentlyTransitioningTo), @"progress": @(nextProgress), @@ -447,7 +447,7 @@ - (void)removeReactSubview:(UIView *)subview - (void)handleTopOfStackChanged { - [_eventDispatcher sendEventWithName:@"topNavigateBack" body:@{ + [_eventDispatcher sendInputEventWithName:@"topNavigateBack" body:@{ @"target":self.reactTag, @"stackLength":@(_navigationController.viewControllers.count) }]; diff --git a/ReactKit/Views/RCTWrapperViewController.m b/ReactKit/Views/RCTWrapperViewController.m index b5425fbcdd01eb..d027dc2f29439e 100644 --- a/ReactKit/Views/RCTWrapperViewController.m +++ b/ReactKit/Views/RCTWrapperViewController.m @@ -96,7 +96,7 @@ - (void)loadView - (void)rightButtonTapped { - [_eventDispatcher sendEventWithName:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}]; + [_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}]; } - (void)didMoveToParentViewController:(UIViewController *)parent diff --git a/packager/react-packager/index.js b/packager/react-packager/index.js index 59a22d6e9e5f1d..65ae88d8d5e158 100644 --- a/packager/react-packager/index.js +++ b/packager/react-packager/index.js @@ -10,10 +10,30 @@ exports.middleware = function(options) { exports.buildPackageFromUrl = function(options, reqUrl) { Activity.disable(); + // Don't start the filewatcher or the cache. + if (options.nonPersistent == null) { + options.nonPersistent = true; + } + var server = new Server(options); return server.buildPackageFromUrl(reqUrl) .then(function(p) { - server.kill(); + server.end(); return p; }); }; + +exports.getDependencies = function(options, main) { + Activity.disable(); + // Don't start the filewatcher or the cache. + if (options.nonPersistent == null) { + options.nonPersistent = true; + } + + var server = new Server(options); + return server.getDependencies(main) + .then(function(r) { + server.end(); + return r.dependencies; + }); +}; diff --git a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js index ca180929da5b3f..9b43f97ee32185 100644 --- a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js @@ -55,6 +55,25 @@ describe('HasteDependencyResolver', function() { isPolyfill: true, dependencies: ['polyfills/prelude.js', 'polyfills/require.js'] }, + { id: 'polyfills/console.js', + isPolyfill: true, + path: 'polyfills/console.js', + dependencies: [ + 'polyfills/prelude.js', + 'polyfills/require.js', + 'polyfills/polyfills.js' + ], + }, + { id: 'polyfills/error-guard.js', + isPolyfill: true, + path: 'polyfills/error-guard.js', + dependencies: [ + 'polyfills/prelude.js', + 'polyfills/require.js', + 'polyfills/polyfills.js', + 'polyfills/console.js' + ], + }, module ]); }); @@ -97,11 +116,35 @@ describe('HasteDependencyResolver', function() { isPolyfill: true, dependencies: ['polyfills/prelude.js', 'polyfills/require.js'] }, + { id: 'polyfills/console.js', + isPolyfill: true, + path: 'polyfills/console.js', + dependencies: [ + 'polyfills/prelude.js', + 'polyfills/require.js', + 'polyfills/polyfills.js' + ], + }, + { id: 'polyfills/error-guard.js', + isPolyfill: true, + path: 'polyfills/error-guard.js', + dependencies: [ + 'polyfills/prelude.js', + 'polyfills/require.js', + 'polyfills/polyfills.js', + 'polyfills/console.js' + ], + }, { path: 'some module', id: 'some module', isPolyfill: true, - dependencies: [ 'polyfills/prelude.js', 'polyfills/require.js', - 'polyfills/polyfills.js'] + dependencies: [ + 'polyfills/prelude.js', + 'polyfills/require.js', + 'polyfills/polyfills.js', + 'polyfills/console.js', + 'polyfills/error-guard.js', + ] }, module ]); diff --git a/packager/react-packager/src/DependencyResolver/haste/index.js b/packager/react-packager/src/DependencyResolver/haste/index.js index 13525bdabdca00..6e2cd6fcac9cbd 100644 --- a/packager/react-packager/src/DependencyResolver/haste/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/index.js @@ -19,7 +19,10 @@ var DEFINE_MODULE_REPLACE_RE = /_moduleName_|_code_|_deps_/g; var REL_REQUIRE_STMT = /require\(['"]([\.\/0-9A-Z_$\-]*)['"]\)/gi; function HasteDependencyResolver(config) { - this._fileWatcher = new FileWatcher(config.projectRoots); + this._fileWatcher = config.nonPersistent + ? FileWatcher.createDummyWatcher() + : new FileWatcher(config.projectRoots); + this._depGraph = new DependencyGraph({ roots: config.projectRoots, ignoreFilePath: function(filepath) { @@ -97,7 +100,6 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) { } } - var relativizedCode = code.replace(REL_REQUIRE_STMT, function(codeMatch, depName) { var dep = resolvedDeps[depName]; @@ -117,7 +119,6 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) { }); }; - HasteDependencyResolver.prototype.end = function() { return this._fileWatcher.end(); }; diff --git a/packager/react-packager/src/FileWatcher/index.js b/packager/react-packager/src/FileWatcher/index.js index c9a48058225c4b..f2721d8cfb9615 100644 --- a/packager/react-packager/src/FileWatcher/index.js +++ b/packager/react-packager/src/FileWatcher/index.js @@ -76,3 +76,11 @@ function createWatcher(root) { }); }); } + +FileWatcher.createDummyWatcher = function() { + var ev = new EventEmitter(); + ev.end = function() { + return q(); + }; + return ev; +}; diff --git a/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js b/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js index fb33a83470f4b4..6c9c66446f2558 100644 --- a/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js +++ b/packager/react-packager/src/JSTransformer/__tests__/Transformer-test.js @@ -6,6 +6,10 @@ jest .dontMock('os') .dontMock('../index'); +var OPTIONS = { + transformModulePath: '/foo/bar' +}; + describe('Transformer', function() { var Transformer; var workers; @@ -32,7 +36,7 @@ describe('Transformer', function() { callback(null, 'content'); }); - return new Transformer({}).loadFileAndTransform([], 'file', {}) + return new Transformer(OPTIONS).loadFileAndTransform([], 'file', {}) .then(function(data) { expect(data).toEqual({ code: 'transformed', @@ -55,7 +59,7 @@ describe('Transformer', function() { callback(null, {error: esprimaError}); }); - return new Transformer({}).loadFileAndTransform([], 'foo-file.js', {}) + return new Transformer(OPTIONS).loadFileAndTransform([], 'foo-file.js', {}) .catch(function(error) { expect(error.type).toEqual('TransformError'); expect(error.snippet).toEqual([ diff --git a/packager/react-packager/src/JSTransformer/index.js b/packager/react-packager/src/JSTransformer/index.js index ce7de143bc36b2..7b01d9617d2b41 100644 --- a/packager/react-packager/src/JSTransformer/index.js +++ b/packager/react-packager/src/JSTransformer/index.js @@ -14,15 +14,21 @@ module.exports = Transformer; Transformer.TransformError = TransformError; function Transformer(projectConfig) { - this._cache = new Cache(projectConfig); - this._workers = workerFarm( - {autoStart: true}, - projectConfig.transformModulePath - ); + this._cache = projectConfig.nonPersistent + ? new DummyCache() : new Cache(projectConfig); + + if (projectConfig.transformModulePath == null) { + this._failedToStart = q.Promise.reject(new Error('No transfrom module')); + } else { + this._workers = workerFarm( + {autoStart: true}, + projectConfig.transformModulePath + ); + } } Transformer.prototype.kill = function() { - workerFarm.end(this._workers); + this._workers && workerFarm.end(this._workers); return this._cache.end(); }; @@ -37,6 +43,10 @@ Transformer.prototype.loadFileAndTransform = function( filePath, options ) { + if (this._failedToStart) { + return this._failedToStart; + } + var workers = this._workers; return this._cache.get(filePath, function() { return readFile(filePath) @@ -93,3 +103,10 @@ function formatEsprimaError(err, filename, source) { error.description = err.description; return error; } + +function DummyCache() {} +DummyCache.prototype.get = function(filePath, loaderCb) { + return loaderCb(); +}; +DummyCache.prototype.end = +DummyCache.prototype.invalidate = function(){}; diff --git a/packager/react-packager/src/Packager/index.js b/packager/react-packager/src/Packager/index.js index 3aef649d89844a..3ec4e378c0f3b8 100644 --- a/packager/react-packager/src/Packager/index.js +++ b/packager/react-packager/src/Packager/index.js @@ -34,15 +34,7 @@ var DEFAULT_CONFIG = { */ polyfillModuleNames: [], - /** - * DEPRECATED - * - * A string of code to be appended to the top of a package. - * - * TODO: THIS RUINS SOURCE MAPS. THIS OPTION SHOULD BE REMOVED ONCE WE GET - * config.polyfillModuleNames WORKING! - */ - runtimeCode: '' + nonPersistent: false, }; function Packager(projectConfig) { @@ -72,7 +64,7 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl) { var findEventId = Activity.startEvent('find dependencies'); var transformEventId; - return this._resolver.getDependencies(main) + return this.getDependencies(main) .then(function(result) { Activity.endEvent(findEventId); transformEventId = Activity.startEvent('transform'); @@ -98,10 +90,14 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl) { }); }; -Packager.prototype.invalidateFile = function(filePath){ +Packager.prototype.invalidateFile = function(filePath) { this._transformer.invalidateFile(filePath); } +Packager.prototype.getDependencies = function(main) { + return this._resolver.getDependencies(main); +}; + Packager.prototype._transformModule = function(module) { var resolver = this._resolver; return this._transformer.loadFileAndTransform( diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index 5545b349fafc9b..26929ebb575ce8 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -18,10 +18,13 @@ function Server(options) { cacheVersion: options.cacheVersion, resetCache: options.resetCache, dev: options.dev, - transformModulePath: options.transformModulePath + transformModulePath: options.transformModulePath, + nonPersistent: options.nonPersistent, }); - this._fileWatcher = new FileWatcher(options.projectRoots); + this._fileWatcher = options.nonPersistent + ? FileWatcher.createDummyWatcher() + : new FileWatcher(options.projectRoots); var onFileChange = this._onFileChange.bind(this); this._fileWatcher.on('all', onFileChange); @@ -48,7 +51,7 @@ Server.prototype._rebuildPackages = function(filepath) { }); }; -Server.prototype.kill = function() { +Server.prototype.end = function() { q.all([ this._fileWatcher.end(), this._packager.kill(), @@ -68,6 +71,10 @@ Server.prototype.buildPackageFromUrl = function(reqUrl) { return this._buildPackage(options); }; +Server.prototype.getDependencies = function(main) { + return this._packager.getDependencies(main); +}; + Server.prototype._processDebugRequest = function(reqUrl, res) { var ret = ''; var pathname = url.parse(reqUrl).pathname;