Merge pull request #1375 from vjeux/Updates_Fri_22_May
Updates fri 22 may
This commit is contained in:
commit
4673dca0b0
|
@ -13,6 +13,7 @@
|
|||
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
|
||||
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1344545A1AAFCAAE003F0779 /* libRCTAdSupport.a */; };
|
||||
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
|
||||
1353F5461B0E64F9009B4FAC /* ClippingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1353F5451B0E64F9009B4FAC /* ClippingTests.m */; };
|
||||
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; };
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
|
||||
|
@ -129,6 +130,7 @@
|
|||
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
|
||||
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAdSupport.xcodeproj; path = ../../Libraries/AdSupport/RCTAdSupport.xcodeproj; sourceTree = "<group>"; };
|
||||
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||
1353F5451B0E64F9009B4FAC /* ClippingTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ClippingTests.m; sourceTree = "<group>"; };
|
||||
139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
||||
13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = UIExplorer/AppDelegate.h; sourceTree = "<group>"; };
|
||||
|
@ -179,6 +181,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
004D28A21AAF61C70097A701 /* UIExplorerTests.m */,
|
||||
1353F5451B0E64F9009B4FAC /* ClippingTests.m */,
|
||||
004D28A01AAF61C70097A701 /* Supporting Files */,
|
||||
);
|
||||
path = UIExplorerTests;
|
||||
|
@ -575,6 +578,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
004D28A31AAF61C70097A701 /* UIExplorerTests.m in Sources */,
|
||||
1353F5461B0E64F9009B4FAC /* ClippingTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIView.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
extern CGRect RCTClipRect(CGSize contentSize, CGFloat contentScale,
|
||||
CGSize targetSize, CGFloat targetScale,
|
||||
UIViewContentMode resizeMode);
|
||||
|
||||
#define RCTAssertEqualPoints(a, b) { \
|
||||
XCTAssertEqual(a.x, b.x); \
|
||||
XCTAssertEqual(a.y, b.y); \
|
||||
}
|
||||
|
||||
#define RCTAssertEqualSizes(a, b) { \
|
||||
XCTAssertEqual(a.width, b.width); \
|
||||
XCTAssertEqual(a.height, b.height); \
|
||||
}
|
||||
|
||||
#define RCTAssertEqualRects(a, b) { \
|
||||
RCTAssertEqualPoints(a.origin, b.origin); \
|
||||
RCTAssertEqualSizes(a.size, b.size); \
|
||||
}
|
||||
|
||||
@interface ClippingTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation ClippingTests
|
||||
|
||||
- (void)testLandscapeSourceLandscapeTarget
|
||||
{
|
||||
CGSize content = {1000, 100};
|
||||
CGSize target = {100, 20};
|
||||
|
||||
{
|
||||
CGRect expected = {CGPointZero, {100, 20}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleToFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
{
|
||||
CGRect expected = {CGPointZero, {100, 10}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFit);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
{
|
||||
CGRect expected = {{-50, 0}, {200, 20}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPortraitSourceLandscapeTarget
|
||||
{
|
||||
CGSize content = {10, 100};
|
||||
CGSize target = {100, 20};
|
||||
|
||||
{
|
||||
CGRect expected = {CGPointZero, {10, 20}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleToFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
{
|
||||
CGRect expected = {CGPointZero, {2, 20}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFit);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
{
|
||||
CGRect expected = {{0, -49}, {10, 100}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testPortraitSourcePortraitTarget
|
||||
{
|
||||
CGSize content = {10, 100};
|
||||
CGSize target = {20, 50};
|
||||
|
||||
{
|
||||
CGRect expected = {CGPointZero, {10, 50}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleToFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
{
|
||||
CGRect expected = {CGPointZero, {5, 50}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFit);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
{
|
||||
CGRect expected = {{0, -37.5}, {10, 100}};
|
||||
CGRect result = RCTClipRect(content, 1, target, 1, UIViewContentModeScaleAspectFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testScaling
|
||||
{
|
||||
CGSize content = {2, 2};
|
||||
CGSize target = {3, 3};
|
||||
|
||||
CGRect expected = {CGPointZero, {3, 3}};
|
||||
CGRect result = RCTClipRect(content, 2, target, 1, UIViewContentModeScaleToFill);
|
||||
RCTAssertEqualRects(expected, result);
|
||||
}
|
||||
|
||||
@end
|
|
@ -1298,6 +1298,14 @@ var Navigator = React.createClass({
|
|||
if (i !== this.state.presentedIndex) {
|
||||
disabledSceneStyle = styles.disabledScene;
|
||||
}
|
||||
var originalRef = child.ref;
|
||||
if (originalRef != null && typeof originalRef !== 'function') {
|
||||
console.warn(
|
||||
'String refs are not supported for navigator scenes. Use a callback ' +
|
||||
'ref instead. Ignoring ref: ' + originalRef
|
||||
);
|
||||
originalRef = null;
|
||||
}
|
||||
return (
|
||||
<View
|
||||
key={this.state.idStack[i]}
|
||||
|
@ -1307,7 +1315,12 @@ var Navigator = React.createClass({
|
|||
}}
|
||||
style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
|
||||
{React.cloneElement(child, {
|
||||
ref: this._handleItemRef.bind(null, this.state.idStack[i], route),
|
||||
ref: component => {
|
||||
this._handleItemRef(this.state.idStack[i], route, component);
|
||||
if (originalRef) {
|
||||
originalRef(component);
|
||||
}
|
||||
}
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -33,6 +33,8 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
|||
- (id)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
block:(RCTImageDownloadBlock)block;
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "RCTImageDownloader.h"
|
||||
|
||||
#import "RCTCache.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *error);
|
||||
|
@ -121,34 +122,134 @@ static NSString *RCTCacheKeyForURL(NSURL *url)
|
|||
}];
|
||||
}
|
||||
|
||||
- (id)downloadImageForURL:(NSURL *)url size:(CGSize)size
|
||||
scale:(CGFloat)scale block:(RCTImageDownloadBlock)block
|
||||
/**
|
||||
* 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){
|
||||
clipRect.size.width + clipRect.origin.x * 2,
|
||||
clipRect.size.height + clipRect.origin.y * 2
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes an input content size & scale (typically from an image),
|
||||
* a target size & scale that it will be drawn into (typically a CGContext) and
|
||||
* 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)
|
||||
{
|
||||
// Precompensate for scale
|
||||
CGFloat scale = sourceScale / destScale;
|
||||
sourceSize.width *= scale;
|
||||
sourceSize.height *= scale;
|
||||
|
||||
// Calculate aspect ratios if needed (don't bother is resizeMode == stretch)
|
||||
CGFloat aspect = 0.0, targetAspect = 0.0;
|
||||
if (resizeMode != UIViewContentModeScaleToFill) {
|
||||
aspect = sourceSize.width / sourceSize.height;
|
||||
targetAspect = destSize.width / destSize.height;
|
||||
if (aspect == targetAspect) {
|
||||
resizeMode = UIViewContentModeScaleToFill;
|
||||
}
|
||||
}
|
||||
|
||||
switch (resizeMode) {
|
||||
case UIViewContentModeScaleToFill: // stretch
|
||||
|
||||
sourceSize.width = MIN(destSize.width, sourceSize.width);
|
||||
sourceSize.height = MIN(destSize.height, sourceSize.height);
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
|
||||
case UIViewContentModeScaleAspectFit: // contain
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
} else { // target is wider than content
|
||||
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
}
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
|
||||
case UIViewContentModeScaleAspectFill: // cover
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
|
||||
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
destSize.width = destSize.height * targetAspect;
|
||||
return (CGRect){{(destSize.width - sourceSize.width) / 2, 0}, sourceSize};
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
destSize.height = destSize.width / targetAspect;
|
||||
return (CGRect){{0, (destSize.height - sourceSize.height) / 2}, sourceSize};
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
|
||||
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) {
|
||||
|
||||
// Resize (TODO: should we take aspect ratio into account?)
|
||||
CGSize imageSize = size;
|
||||
if (CGSizeEqualToSize(imageSize, CGSizeZero)) {
|
||||
imageSize = image.size;
|
||||
} else {
|
||||
imageSize = (CGSize){
|
||||
MIN(size.width, image.size.width),
|
||||
MIN(size.height, image.size.height)
|
||||
};
|
||||
}
|
||||
// Get scale and size
|
||||
CGFloat destScale = scale ?: RCTScreenScale();
|
||||
CGRect imageRect = RCTClipRect(image.size, image.scale, size, destScale, resizeMode);
|
||||
CGSize destSize = RCTTargetSizeForClipRect(imageRect);
|
||||
|
||||
// Rescale image if required size is smaller
|
||||
CGFloat imageScale = scale;
|
||||
if (imageScale == 0 || imageScale < image.scale) {
|
||||
imageScale = image.scale;
|
||||
// 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(imageSize, NO, imageScale);
|
||||
[image drawInRect:(CGRect){{0, 0}, imageSize}];
|
||||
UIGraphicsBeginImageContextWithOptions(destSize, opaque, destScale);
|
||||
if (blendColor) {
|
||||
[blendColor setFill];
|
||||
UIRectFill((CGRect){CGPointZero, destSize});
|
||||
}
|
||||
[image drawInRect:imageRect];
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue = dispatch_queue_create("com.facebook.rctImageLoader", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_set_target_queue(queue,
|
||||
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
|
||||
});
|
||||
|
||||
return queue;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageDownloader.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@implementation RCTNetworkImageView
|
||||
{
|
||||
|
@ -26,8 +27,7 @@
|
|||
|
||||
- (instancetype)initWithFrame:(CGRect)frame imageDownloader:(RCTImageDownloader *)imageDownloader
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
_deferSentinel = 0;
|
||||
_imageDownloader = imageDownloader;
|
||||
self.userInteractionEnabled = NO;
|
||||
|
@ -37,20 +37,44 @@
|
|||
|
||||
- (NSURL *)imageURL
|
||||
{
|
||||
// We clear our backing layer's imageURL when we are not in a window for a while,
|
||||
// We clear our imageURL when we are not in a window for a while,
|
||||
// to make sure we don't consume network resources while offscreen.
|
||||
// However we don't want to expose this hackery externally.
|
||||
return _deferred ? _deferredImageURL : _imageURL;
|
||||
}
|
||||
|
||||
- (void)setBackgroundColor:(UIColor *)backgroundColor
|
||||
{
|
||||
super.backgroundColor = backgroundColor;
|
||||
[self _updateImage];
|
||||
}
|
||||
|
||||
- (void)reactSetFrame:(CGRect)frame
|
||||
{
|
||||
[super reactSetFrame:frame];
|
||||
[self _updateImage];
|
||||
}
|
||||
|
||||
- (void)_updateImage
|
||||
{
|
||||
[self setImageURL:_imageURL resetToDefaultImageWhileLoading:NO];
|
||||
}
|
||||
|
||||
- (void)setImageURL:(NSURL *)imageURL resetToDefaultImageWhileLoading:(BOOL)reset
|
||||
{
|
||||
if (![_imageURL isEqual:imageURL] && _downloadToken) {
|
||||
[_imageDownloader cancelDownload:_downloadToken];
|
||||
_downloadToken = nil;
|
||||
}
|
||||
|
||||
_imageURL = imageURL;
|
||||
|
||||
if (_deferred) {
|
||||
_deferredImageURL = imageURL;
|
||||
} else {
|
||||
if (_downloadToken) {
|
||||
[_imageDownloader cancelDownload:_downloadToken];
|
||||
_downloadToken = nil;
|
||||
if (!imageURL) {
|
||||
self.layer.contents = nil;
|
||||
return;
|
||||
}
|
||||
if (reset) {
|
||||
self.layer.contentsScale = _defaultImage.scale;
|
||||
|
@ -62,25 +86,35 @@
|
|||
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) {
|
||||
if (data) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (imageURL != self.imageURL) {
|
||||
// Image has changed
|
||||
return;
|
||||
}
|
||||
CAKeyframeAnimation *animation = RCTGIFImageWithData(data);
|
||||
self.layer.contentsScale = 1.0;
|
||||
self.layer.minificationFilter = kCAFilterLinear;
|
||||
self.layer.magnificationFilter = kCAFilterLinear;
|
||||
[self.layer addAnimation:animation forKey:@"contents"];
|
||||
});
|
||||
} else if (error) {
|
||||
RCTLogWarn(@"Unable to download image data. Error: %@", error);
|
||||
}
|
||||
// TODO: handle errors
|
||||
}];
|
||||
} else {
|
||||
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() block:^(UIImage *image, NSError *error) {
|
||||
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() resizeMode:self.contentMode backgroundColor:self.backgroundColor block:^(UIImage *image, NSError *error) {
|
||||
if (image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (imageURL != self.imageURL) {
|
||||
// Image has changed
|
||||
return;
|
||||
}
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.layer.contentsScale = image.scale;
|
||||
self.layer.contents = (__bridge id)image.CGImage;
|
||||
});
|
||||
} else if (error) {
|
||||
RCTLogWarn(@"Unable to download image. Error: %@", error);
|
||||
}
|
||||
// TODO: handle errors
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,13 @@ var invariant = require('invariant');
|
|||
var keyMirror = require('keyMirror');
|
||||
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();
|
||||
|
@ -83,17 +90,25 @@ var InteractionManager = {
|
|||
/**
|
||||
* Notify manager that an interaction has started.
|
||||
*/
|
||||
createInteractionHandle(): number {
|
||||
createInteractionHandle(): Handle {
|
||||
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;
|
||||
},
|
||||
|
||||
/**
|
||||
* Notify manager that an interaction has completed.
|
||||
*/
|
||||
clearInteractionHandle(handle: number) {
|
||||
clearInteractionHandle(handle: Handle) {
|
||||
invariant(
|
||||
!!handle,
|
||||
'Must provide a handle to clear.'
|
||||
|
@ -151,4 +166,19 @@ 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;
|
||||
|
|
|
@ -43,7 +43,6 @@ var JSTimers = {
|
|||
var newID = JSTimersExecution.GUID++;
|
||||
var freeIndex = JSTimers._getFreeIndex();
|
||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
||||
JSTimersExecution.callbacks[freeIndex] = func;
|
||||
JSTimersExecution.callbacks[freeIndex] = function() {
|
||||
return func.apply(undefined, args);
|
||||
};
|
||||
|
@ -60,12 +59,15 @@ var JSTimers = {
|
|||
var newID = JSTimersExecution.GUID++;
|
||||
var freeIndex = JSTimers._getFreeIndex();
|
||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
||||
JSTimersExecution.callbacks[freeIndex] = func;
|
||||
JSTimersExecution.callbacks[freeIndex] = function() {
|
||||
return func.apply(undefined, args);
|
||||
var startTime = Date.now();
|
||||
var ret = func.apply(undefined, args);
|
||||
var endTime = Date.now();
|
||||
RCTTiming.createTimer(newID, Math.max(0, duration - (endTime - startTime)), endTime, false);
|
||||
return ret;
|
||||
};
|
||||
JSTimersExecution.types[freeIndex] = JSTimersExecution.Type.setInterval;
|
||||
RCTTiming.createTimer(newID, duration, Date.now(), /** recurring */ true);
|
||||
RCTTiming.createTimer(newID, duration, Date.now(), /** recurring */ false);
|
||||
return newID;
|
||||
},
|
||||
|
||||
|
@ -77,7 +79,6 @@ var JSTimers = {
|
|||
var newID = JSTimersExecution.GUID++;
|
||||
var freeIndex = JSTimers._getFreeIndex();
|
||||
JSTimersExecution.timerIDs[freeIndex] = newID;
|
||||
JSTimersExecution.callbacks[freeIndex] = func;
|
||||
JSTimersExecution.callbacks[freeIndex] = function() {
|
||||
return func.apply(undefined, args);
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#import "RCTJavaScriptLoader.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTPerfStats.h"
|
||||
#import "RCTProfile.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTRootView.h"
|
||||
|
@ -930,6 +931,11 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
_queuesByID = [[RCTSparseArray alloc] init];
|
||||
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
|
||||
|
||||
if (RCT_DEV) {
|
||||
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
||||
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize executor to allow enqueueing calls
|
||||
*/
|
||||
|
@ -1560,6 +1566,8 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
}
|
||||
|
||||
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||
|
||||
[self.perfStats.jsGraph tick:displayLink.timestamp];
|
||||
}
|
||||
|
||||
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
||||
|
@ -1567,6 +1575,8 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
RCTAssertMainThread();
|
||||
|
||||
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
||||
|
||||
[self.perfStats.uiGraph tick:displayLink.timestamp];
|
||||
}
|
||||
|
||||
- (void)startProfiling
|
||||
|
@ -1578,10 +1588,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
return;
|
||||
}
|
||||
|
||||
[_mainDisplayLink invalidate];
|
||||
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
||||
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
|
||||
RCTProfileInit();
|
||||
}
|
||||
|
||||
|
@ -1589,8 +1595,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
[_mainDisplayLink invalidate];
|
||||
|
||||
NSString *log = RCTProfileEnd();
|
||||
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
*/
|
||||
@property (nonatomic, assign) BOOL liveReloadEnabled;
|
||||
|
||||
/**
|
||||
* Shows the FPS monitor for the JS and Main threads
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL showFPS;
|
||||
|
||||
/**
|
||||
* Manually show the dev menu (can be called from JS).
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#import "RCTDefines.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTPerfStats.h"
|
||||
#import "RCTProfile.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTSourceCode.h"
|
||||
|
@ -145,6 +146,7 @@ RCT_EXPORT_MODULE()
|
|||
self.shakeToShow = [_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||
self.profilingEnabled = [_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||
self.liveReloadEnabled = [_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||
self.showFPS = [_settings[@"showFPS"] ?: @NO boolValue];
|
||||
self.executorClass = NSClassFromString(_settings[@"executorClass"]);
|
||||
}
|
||||
|
||||
|
@ -230,13 +232,14 @@ RCT_EXPORT_METHOD(show)
|
|||
|
||||
NSString *debugTitleChrome = _executorClass && _executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Debug in Chrome";
|
||||
NSString *debugTitleSafari = _executorClass && _executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Debug in Safari";
|
||||
NSString *fpsMonitor = _showFPS ? @"Hide FPS Monitor" : @"Show FPS Monitor";
|
||||
|
||||
UIActionSheet *actionSheet =
|
||||
[[UIActionSheet alloc] initWithTitle:@"React Native: Development"
|
||||
delegate:self
|
||||
cancelButtonTitle:nil
|
||||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, nil];
|
||||
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, fpsMonitor, nil];
|
||||
|
||||
if (_liveReloadURL) {
|
||||
|
||||
|
@ -293,10 +296,14 @@ RCT_EXPORT_METHOD(reload)
|
|||
break;
|
||||
}
|
||||
case 3: {
|
||||
self.liveReloadEnabled = !_liveReloadEnabled;
|
||||
self.showFPS = !_showFPS;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
self.liveReloadEnabled = !_liveReloadEnabled;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
self.profilingEnabled = !_profilingEnabled;
|
||||
break;
|
||||
}
|
||||
|
@ -368,6 +375,21 @@ RCT_EXPORT_METHOD(reload)
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setShowFPS:(BOOL)showFPS
|
||||
{
|
||||
if (_showFPS != showFPS) {
|
||||
_showFPS = showFPS;
|
||||
|
||||
if (showFPS) {
|
||||
[_bridge.perfStats show];
|
||||
} else {
|
||||
[_bridge.perfStats hide];
|
||||
}
|
||||
|
||||
[self updateSetting:@"showFPS" value:@(showFPS)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkForUpdates
|
||||
{
|
||||
if (!_jsLoaded || !_liveReloadEnabled || !_liveReloadURL) {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* 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 <UIKit/UIKit.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, RCTFPSGraphPosition) {
|
||||
RCTFPSGraphPositionLeft = 1,
|
||||
RCTFPSGraphPositionRight = 2
|
||||
};
|
||||
|
||||
@interface RCTFPSGraph : UIView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame graphPosition:(RCTFPSGraphPosition)position name:(NSString *)name color:(UIColor *)color NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)tick:(NSTimeInterval)timestamp;
|
||||
|
||||
@end
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* 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 "RCTFPSGraph.h"
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
#if RCT_DEV
|
||||
|
||||
@implementation RCTFPSGraph
|
||||
{
|
||||
CAShapeLayer *_graph;
|
||||
NSString *_name;
|
||||
NSTimeInterval _prevTime;
|
||||
RCTFPSGraphPosition _position;
|
||||
UILabel *_label;
|
||||
|
||||
float *_frames;
|
||||
int _frameCount;
|
||||
int _maxFPS;
|
||||
int _minFPS;
|
||||
int _length;
|
||||
int _margin;
|
||||
int _height;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame graphPosition:(RCTFPSGraphPosition)position name:(NSString *)name color:(UIColor *)color
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
_margin = 2;
|
||||
_prevTime = -1;
|
||||
_maxFPS = 0;
|
||||
_minFPS = 60;
|
||||
_length = (frame.size.width - 2 * _margin) / 2;
|
||||
_height = frame.size.height - 2 * _margin;
|
||||
_frames = malloc(sizeof(float) * _length);
|
||||
memset(_frames, 0, sizeof(float) * _length);
|
||||
|
||||
_name = name;
|
||||
_position = position;
|
||||
_graph = [self createGraph:color];
|
||||
_label = [self createLabel:color];
|
||||
|
||||
[self addSubview:_label];
|
||||
[self.layer addSublayer:_graph];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
free(_frames);
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
}
|
||||
|
||||
- (CAShapeLayer *)createGraph:(UIColor *)color
|
||||
{
|
||||
CGFloat left = _position & RCTFPSGraphPositionLeft ? 0 : _length;
|
||||
CAShapeLayer *graph = [[CAShapeLayer alloc] init];
|
||||
graph.frame = CGRectMake(left, 0, 2 * _margin + _length, self.frame.size.height);
|
||||
graph.backgroundColor = [[color colorWithAlphaComponent:.2] CGColor];
|
||||
graph.fillColor = [color CGColor];
|
||||
return graph;
|
||||
}
|
||||
|
||||
- (UILabel *)createLabel:(UIColor *)color
|
||||
{
|
||||
CGFloat left = _position & RCTFPSGraphPositionLeft ? 2 * _margin + _length : 0;
|
||||
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(left, 0, _length, self.frame.size.height)];
|
||||
label.textColor = color;
|
||||
label.font = [UIFont systemFontOfSize:9];
|
||||
label.minimumScaleFactor = .5;
|
||||
label.adjustsFontSizeToFitWidth = YES;
|
||||
label.numberOfLines = 3;
|
||||
label.lineBreakMode = NSLineBreakByWordWrapping;
|
||||
label.textAlignment = NSTextAlignmentCenter;
|
||||
return label;
|
||||
}
|
||||
|
||||
- (void)tick:(NSTimeInterval)timestamp
|
||||
{
|
||||
_frameCount++;
|
||||
if (_prevTime == -1) {
|
||||
_prevTime = timestamp;
|
||||
} else if (timestamp - _prevTime > 1) {
|
||||
float fps = round(_frameCount / (timestamp - _prevTime));
|
||||
_minFPS = MIN(_minFPS, fps);
|
||||
_maxFPS = MAX(_maxFPS, fps);
|
||||
|
||||
_label.text = [NSString stringWithFormat:@"%@\n%d FPS\n(%d - %d)", _name, (int)fps, _minFPS, _maxFPS];
|
||||
|
||||
float scale = 60.0 / _height;
|
||||
for (int i = 0; i < _length - 1; i++) {
|
||||
_frames[i] = _frames[i + 1];
|
||||
}
|
||||
_frames[_length - 1] = fps / scale;
|
||||
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
if (_position & RCTFPSGraphPositionLeft) {
|
||||
CGPathMoveToPoint(path, NULL, _margin, _margin + _height);
|
||||
for (int i = 0; i < _length; i++) {
|
||||
CGPathAddLineToPoint(path, NULL, _margin + i, _margin + _height - _frames[i]);
|
||||
}
|
||||
CGPathAddLineToPoint(path, NULL, _margin + _length - 1, _margin + _height);
|
||||
} else {
|
||||
CGPathMoveToPoint(path, NULL, _margin + _length - 1, _margin + _height);
|
||||
for (int i = 0; i < _length; i++) {
|
||||
CGPathAddLineToPoint(path, NULL, _margin + _length - i - 1, _margin + _height - _frames[i]);
|
||||
}
|
||||
CGPathAddLineToPoint(path, NULL, _margin, _margin + _height);
|
||||
}
|
||||
_graph.path = path;
|
||||
CGPathRelease(path);
|
||||
|
||||
_prevTime = timestamp;
|
||||
_frameCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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 "RCTBridge.h"
|
||||
#import "RCTFPSGraph.h"
|
||||
|
||||
@interface RCTPerfStats : NSObject
|
||||
|
||||
@property (nonatomic, strong) RCTFPSGraph *jsGraph;
|
||||
@property (nonatomic, strong) RCTFPSGraph *uiGraph;
|
||||
|
||||
- (void)show;
|
||||
- (void)hide;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTPerfStats)
|
||||
|
||||
@property (nonatomic, strong, readonly) RCTPerfStats *perfStats;
|
||||
|
||||
@end
|
|
@ -0,0 +1,133 @@
|
|||
/**
|
||||
* 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 "RCTPerfStats.h"
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
#if RCT_DEV
|
||||
|
||||
@interface RCTPerfStats() <RCTBridgeModule>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTPerfStats
|
||||
{
|
||||
UIView *_container;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self hide];
|
||||
}
|
||||
|
||||
- (UIView *)container
|
||||
{
|
||||
if (!_container) {
|
||||
_container = [[UIView alloc] init];
|
||||
_container.backgroundColor = [UIColor colorWithRed:0 green:0 blue:34/255.0 alpha:1];
|
||||
_container.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
|
||||
}
|
||||
return _container;
|
||||
}
|
||||
|
||||
- (RCTFPSGraph *)jsGraph
|
||||
{
|
||||
if (!_jsGraph) {
|
||||
UIColor *jsColor = [UIColor colorWithRed:0 green:1 blue:0 alpha:1];
|
||||
_jsGraph = [[RCTFPSGraph alloc] initWithFrame:CGRectMake(2, 2, 124, 34)
|
||||
graphPosition:RCTFPSGraphPositionRight
|
||||
name:@"[ JS ]"
|
||||
color:jsColor];
|
||||
_jsGraph.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
||||
}
|
||||
return _jsGraph;
|
||||
}
|
||||
|
||||
- (RCTFPSGraph *)uiGraph
|
||||
{
|
||||
if (!_uiGraph) {
|
||||
UIColor *uiColor = [UIColor colorWithRed:0 green:1 blue:1 alpha:1];
|
||||
_uiGraph = [[RCTFPSGraph alloc] initWithFrame:CGRectMake(2, 2, 124, 34)
|
||||
graphPosition:RCTFPSGraphPositionLeft
|
||||
name:@"[ UI ]"
|
||||
color:uiColor];
|
||||
}
|
||||
return _uiGraph;
|
||||
}
|
||||
|
||||
- (void)show
|
||||
{
|
||||
UIView *targetView = [[[[[UIApplication sharedApplication] delegate] window] rootViewController] view];
|
||||
|
||||
targetView.frame = (CGRect){
|
||||
targetView.frame.origin,
|
||||
{
|
||||
targetView.frame.size.width,
|
||||
targetView.frame.size.height - 38,
|
||||
}
|
||||
};
|
||||
|
||||
self.container.frame = (CGRect){{0, targetView.frame.size.height}, {targetView.frame.size.width, 38}};
|
||||
self.jsGraph.frame = (CGRect){
|
||||
{
|
||||
targetView.frame.size.width - self.uiGraph.frame.size.width - self.uiGraph.frame.origin.x,
|
||||
self.uiGraph.frame.origin.x,
|
||||
},
|
||||
self.uiGraph.frame.size,
|
||||
};
|
||||
|
||||
[self.container addSubview:self.jsGraph];
|
||||
[self.container addSubview:self.uiGraph];
|
||||
[targetView addSubview:self.container];
|
||||
}
|
||||
|
||||
- (void)hide
|
||||
{
|
||||
UIView *targetView = _container.superview;
|
||||
|
||||
targetView.frame = (CGRect){
|
||||
targetView.frame.origin,
|
||||
{
|
||||
targetView.frame.size.width,
|
||||
targetView.frame.size.height + _container.frame.size.height
|
||||
}
|
||||
};
|
||||
|
||||
[_container removeFromSuperview];
|
||||
}
|
||||
|
||||
- (dispatch_queue_t)methodQueue
|
||||
{
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
||||
@implementation RCTPerfStats
|
||||
|
||||
- (void)show {}
|
||||
- (void)hide {}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@implementation RCTBridge (RCTPerfStats)
|
||||
|
||||
- (RCTPerfStats *)perfStats
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTPerfStats class])];
|
||||
}
|
||||
|
||||
@end
|
|
@ -52,3 +52,6 @@ RCT_EXTERN NSDictionary *RCTMakeAndLogError(NSString *message, id toStringify, N
|
|||
|
||||
// Returns YES if React is running in a test environment
|
||||
RCT_EXTERN BOOL RCTRunningInTestEnvironment(void);
|
||||
|
||||
// Return YES if image has an alpha component
|
||||
RCT_EXTERN BOOL RCTImageHasAlpha(CGImageRef image);
|
||||
|
|
|
@ -261,3 +261,15 @@ BOOL RCTRunningInTestEnvironment(void)
|
|||
});
|
||||
return _isTestEnvironment;
|
||||
}
|
||||
|
||||
BOOL RCTImageHasAlpha(CGImageRef image)
|
||||
{
|
||||
switch (CGImageGetAlphaInfo(image)) {
|
||||
case kCGImageAlphaNone:
|
||||
case kCGImageAlphaNoneSkipLast:
|
||||
case kCGImageAlphaNoneSkipFirst:
|
||||
return NO;
|
||||
default:
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,11 @@
|
|||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
|
||||
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067501A70F44B002CDEE1 /* RCTView.m */; };
|
||||
13E067591A70F44B002CDEE1 /* UIView+React.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+React.m */; };
|
||||
1403F2B31B0AE60700C2A9A4 /* RCTPerfStats.m in Sources */ = {isa = PBXBuildFile; fileRef = 1403F2B21B0AE60700C2A9A4 /* RCTPerfStats.m */; };
|
||||
14200DAA1AC179B3008EE6BA /* RCTJavaScriptLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */; };
|
||||
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE21AAC4AE100FC20F4 /* RCTMap.m */; };
|
||||
14435CE61AAC4AE100FC20F4 /* RCTMapManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */; };
|
||||
146459261B06C49500B389AA /* RCTFPSGraph.m in Sources */ = {isa = PBXBuildFile; fileRef = 146459251B06C49500B389AA /* RCTFPSGraph.m */; };
|
||||
14F3620D1AABD06A001CE568 /* RCTSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F362081AABD06A001CE568 /* RCTSwitch.m */; };
|
||||
14F3620E1AABD06A001CE568 /* RCTSwitchManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F3620A1AABD06A001CE568 /* RCTSwitchManager.m */; };
|
||||
14F484561AABFCE100FDF6B9 /* RCTSliderManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 14F484551AABFCE100FDF6B9 /* RCTSliderManager.m */; };
|
||||
|
@ -163,6 +165,8 @@
|
|||
13E067501A70F44B002CDEE1 /* RCTView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTView.m; sourceTree = "<group>"; };
|
||||
13E067531A70F44B002CDEE1 /* UIView+React.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+React.h"; sourceTree = "<group>"; };
|
||||
13E067541A70F44B002CDEE1 /* UIView+React.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+React.m"; sourceTree = "<group>"; };
|
||||
1403F2B11B0AE60700C2A9A4 /* RCTPerfStats.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPerfStats.h; sourceTree = "<group>"; };
|
||||
1403F2B21B0AE60700C2A9A4 /* RCTPerfStats.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPerfStats.m; sourceTree = "<group>"; };
|
||||
14200DA81AC179B3008EE6BA /* RCTJavaScriptLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptLoader.h; sourceTree = "<group>"; };
|
||||
14200DA91AC179B3008EE6BA /* RCTJavaScriptLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptLoader.m; sourceTree = "<group>"; };
|
||||
1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTFrameUpdate.h; sourceTree = "<group>"; };
|
||||
|
@ -170,6 +174,8 @@
|
|||
14435CE21AAC4AE100FC20F4 /* RCTMap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMap.m; sourceTree = "<group>"; };
|
||||
14435CE31AAC4AE100FC20F4 /* RCTMapManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMapManager.h; sourceTree = "<group>"; };
|
||||
14435CE41AAC4AE100FC20F4 /* RCTMapManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMapManager.m; sourceTree = "<group>"; };
|
||||
146459241B06C49500B389AA /* RCTFPSGraph.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFPSGraph.h; sourceTree = "<group>"; };
|
||||
146459251B06C49500B389AA /* RCTFPSGraph.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFPSGraph.m; sourceTree = "<group>"; };
|
||||
14F362071AABD06A001CE568 /* RCTSwitch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitch.h; sourceTree = "<group>"; };
|
||||
14F362081AABD06A001CE568 /* RCTSwitch.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSwitch.m; sourceTree = "<group>"; };
|
||||
14F362091AABD06A001CE568 /* RCTSwitchManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSwitchManager.h; sourceTree = "<group>"; };
|
||||
|
@ -415,6 +421,10 @@
|
|||
1436DD071ADE7AA000A5ED7D /* RCTFrameUpdate.h */,
|
||||
14F4D3891AE1B7E40049C042 /* RCTProfile.h */,
|
||||
14F4D38A1AE1B7E40049C042 /* RCTProfile.m */,
|
||||
146459241B06C49500B389AA /* RCTFPSGraph.h */,
|
||||
146459251B06C49500B389AA /* RCTFPSGraph.m */,
|
||||
1403F2B11B0AE60700C2A9A4 /* RCTPerfStats.h */,
|
||||
1403F2B21B0AE60700C2A9A4 /* RCTPerfStats.m */,
|
||||
);
|
||||
path = Base;
|
||||
sourceTree = "<group>";
|
||||
|
@ -514,6 +524,7 @@
|
|||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */,
|
||||
58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */,
|
||||
13B080061A6947C200A75B9A /* RCTScrollViewManager.m in Sources */,
|
||||
146459261B06C49500B389AA /* RCTFPSGraph.m in Sources */,
|
||||
14200DAA1AC179B3008EE6BA /* RCTJavaScriptLoader.m in Sources */,
|
||||
137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */,
|
||||
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
|
||||
|
@ -549,6 +560,7 @@
|
|||
14435CE51AAC4AE100FC20F4 /* RCTMap.m in Sources */,
|
||||
134FCB3E1A6E7F0800051CC8 /* RCTWebViewExecutor.m in Sources */,
|
||||
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,
|
||||
1403F2B31B0AE60700C2A9A4 /* RCTPerfStats.m in Sources */,
|
||||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */,
|
||||
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */,
|
||||
13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */,
|
||||
|
|
|
@ -127,7 +127,6 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
_borderBottomRightRadius = -1;
|
||||
|
||||
_backgroundColor = [super backgroundColor];
|
||||
[super setBackgroundColor:[UIColor clearColor]];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -443,6 +442,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
|
||||
- (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
|
||||
{
|
||||
static const CGFloat threshold = 0.001;
|
||||
|
||||
const CGFloat maxRadius = MIN(self.bounds.size.height, self.bounds.size.width);
|
||||
const CGFloat radius = MAX(0, _borderRadius);
|
||||
const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
|
||||
|
@ -456,6 +457,17 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
const CGFloat bottomWidth = _borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth;
|
||||
const CGFloat leftWidth = _borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth;
|
||||
|
||||
if (topLeftRadius < threshold &&
|
||||
topRightRadius < threshold &&
|
||||
bottomLeftRadius < threshold &&
|
||||
bottomRightRadius < threshold &&
|
||||
topWidth < threshold &&
|
||||
rightWidth < threshold &&
|
||||
bottomWidth < threshold &&
|
||||
leftWidth < threshold) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
const CGFloat innerTopLeftRadiusX = MAX(0, topLeftRadius - leftWidth);
|
||||
const CGFloat innerTopLeftRadiusY = MAX(0, topLeftRadius - topWidth);
|
||||
|
||||
|
@ -657,22 +669,21 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
|
||||
- (void)displayLayer:(CALayer *)layer
|
||||
{
|
||||
CGRect contentsCenter;
|
||||
CGRect contentsCenter = (CGRect){CGPointZero, {1, 1}};
|
||||
UIImage *image = [self generateBorderImage:&contentsCenter];
|
||||
|
||||
if (RCTRunningInTestEnvironment()) {
|
||||
if (image && RCTRunningInTestEnvironment()) {
|
||||
const CGSize size = self.bounds.size;
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
|
||||
[image drawInRect:(CGRect){CGPointZero, size}];
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
contentsCenter = CGRectMake(0, 0, 1, 1);
|
||||
}
|
||||
|
||||
layer.backgroundColor = [image ? [UIColor clearColor] : _backgroundColor CGColor];
|
||||
layer.contents = (id)image.CGImage;
|
||||
layer.contentsCenter = contentsCenter;
|
||||
layer.contentsScale = image.scale;
|
||||
layer.contentsScale = image.scale ?: 1.0;
|
||||
layer.magnificationFilter = kCAFilterNearest;
|
||||
}
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
"stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638",
|
||||
"uglify-js": "~2.4.16",
|
||||
"underscore": "1.7.0",
|
||||
"worker-farm": "^1.3.0",
|
||||
"worker-farm": "^1.3.1",
|
||||
"ws": "0.4.31",
|
||||
"yargs": "1.3.2"
|
||||
},
|
||||
|
|
|
@ -24,12 +24,21 @@ function parseCommandLine(config) {
|
|||
// optimist default API requires you to write the command name three time
|
||||
// This is a small wrapper to accept an object instead
|
||||
for (var i = 0; i < config.length; ++i) {
|
||||
if (config[i].type === 'string') {
|
||||
optimist.string(config[i].command);
|
||||
} else {
|
||||
optimist.boolean(config[i].command);
|
||||
}
|
||||
|
||||
optimist
|
||||
.boolean(config[i].command)
|
||||
.default(config[i].command, config[i].default)
|
||||
.describe(config[i].command, config[i].description);
|
||||
|
||||
if (config[i].required) {
|
||||
optimist.demand(config[i].command);
|
||||
}
|
||||
}
|
||||
var argv = optimist.argv;
|
||||
var argv = optimist.parse(process.argv);
|
||||
|
||||
// optimist doesn't have support for --dev=false, instead it returns 'false'
|
||||
for (var i = 0; i < config.length; ++i) {
|
||||
|
@ -43,6 +52,15 @@ function parseCommandLine(config) {
|
|||
if (argv[command] === 'false') {
|
||||
argv[command] = false;
|
||||
}
|
||||
if (config[i].type === 'string') {
|
||||
// According to https://github.com/substack/node-optimist#numbers,
|
||||
// every argument that looks like a number should be converted to one.
|
||||
var strValue = argv[command];
|
||||
var numValue = strValue ? Number(strValue) : undefined;
|
||||
if (typeof numValue === 'number' && !isNaN(numValue)) {
|
||||
argv[command] = numValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show --help
|
||||
|
|
|
@ -47,6 +47,32 @@ describe('AssetServer', function() {
|
|||
});
|
||||
});
|
||||
|
||||
pit('should work for the simple case with jpg', function() {
|
||||
var server = new AssetServer({
|
||||
projectRoots: ['/root'],
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
imgs: {
|
||||
'b.png': 'png image',
|
||||
'b.jpg': 'jpeg image',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
server.get('imgs/b.jpg'),
|
||||
server.get('imgs/b.png'),
|
||||
]).then(function(data) {
|
||||
expect(data).toEqual([
|
||||
'jpeg image',
|
||||
'png image',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('should pick the bigger one', function() {
|
||||
var server = new AssetServer({
|
||||
projectRoots: ['/root'],
|
||||
|
@ -136,5 +162,45 @@ describe('AssetServer', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
pit('should get assetData for non-png images', function() {
|
||||
var hash = {
|
||||
update: jest.genMockFn(),
|
||||
digest: jest.genMockFn(),
|
||||
};
|
||||
|
||||
hash.digest.mockImpl(function() {
|
||||
return 'wow such hash';
|
||||
});
|
||||
crypto.createHash.mockImpl(function() {
|
||||
return hash;
|
||||
});
|
||||
|
||||
var server = new AssetServer({
|
||||
projectRoots: ['/root'],
|
||||
assetExts: ['png', 'jpeg'],
|
||||
});
|
||||
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
imgs: {
|
||||
'b@1x.jpg': 'b1 image',
|
||||
'b@2x.jpg': 'b2 image',
|
||||
'b@4x.jpg': 'b4 image',
|
||||
'b@4.5x.jpg': 'b4.5 image',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return server.getAssetData('imgs/b.jpg').then(function(data) {
|
||||
expect(hash.update.mock.calls.length).toBe(4);
|
||||
expect(data).toEqual({
|
||||
type: 'jpg',
|
||||
name: 'b',
|
||||
scales: [1, 2, 4, 4.5],
|
||||
hash: 'wow such hash',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,7 +28,7 @@ var validateOpts = declareOpts({
|
|||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
default: ['png'],
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -90,7 +90,7 @@ AssetServer.prototype.getAssetData = function(assetPath) {
|
|||
var nameData = getAssetDataFromName(assetPath);
|
||||
var data = {
|
||||
name: nameData.name,
|
||||
type: 'png',
|
||||
type: nameData.type,
|
||||
};
|
||||
|
||||
return this._getAssetRecord(assetPath).then(function(record) {
|
||||
|
|
|
@ -55,7 +55,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -91,7 +92,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -121,7 +123,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -161,6 +164,7 @@ describe('DependencyGraph', function() {
|
|||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
assetRoots_DEPRECATED: ['/root/imgs'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
|
@ -199,6 +203,7 @@ describe('DependencyGraph', function() {
|
|||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -246,6 +251,7 @@ describe('DependencyGraph', function() {
|
|||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -308,6 +314,7 @@ describe('DependencyGraph', function() {
|
|||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
assetRoots_DEPRECATED: ['/root/imgs'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
|
@ -358,7 +365,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -391,7 +399,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -421,7 +430,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -455,7 +465,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -489,7 +500,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -519,7 +531,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -552,7 +565,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -595,7 +609,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/somedir/somefile.js'))
|
||||
|
@ -641,7 +656,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -674,7 +690,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -712,7 +729,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -755,7 +773,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -798,7 +817,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -847,7 +867,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -888,7 +909,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -931,7 +953,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -974,7 +997,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -1027,7 +1051,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -1092,7 +1117,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
expect(dgraph.getOrderedDependencies('/root/index.js'))
|
||||
|
@ -1158,7 +1184,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
filesystem.root['index.js'] =
|
||||
|
@ -1209,7 +1236,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
filesystem.root['index.js'] =
|
||||
|
@ -1260,7 +1288,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
delete filesystem.root.foo;
|
||||
|
@ -1310,7 +1339,8 @@ describe('DependencyGraph', function() {
|
|||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
filesystem.root['bar.js'] = [
|
||||
|
@ -1367,7 +1397,7 @@ describe('DependencyGraph', function() {
|
|||
roots: [root],
|
||||
assetRoots_DEPRECATED: [root],
|
||||
assetExts: ['png'],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
});
|
||||
|
||||
return dgraph.load().then(function() {
|
||||
|
@ -1419,7 +1449,7 @@ describe('DependencyGraph', function() {
|
|||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
assetExts: ['png'],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
});
|
||||
|
||||
return dgraph.load().then(function() {
|
||||
|
@ -1482,6 +1512,7 @@ describe('DependencyGraph', function() {
|
|||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
ignoreFilePath: function(filePath) {
|
||||
if (filePath === '/root/bar.js') {
|
||||
return true;
|
||||
|
@ -1550,7 +1581,8 @@ describe('DependencyGraph', function() {
|
|||
});
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
});
|
||||
return dgraph.load().then(function() {
|
||||
triggerFileChange('change', 'aPackage', '/root', {
|
||||
|
|
|
@ -44,7 +44,7 @@ var validateOpts = declareOpts({
|
|||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
default: ['png'],
|
||||
required: true,
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ var validateOpts = declareOpts({
|
|||
type: 'object',
|
||||
required: true,
|
||||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
}
|
||||
});
|
||||
|
||||
function HasteDependencyResolver(options) {
|
||||
|
@ -62,6 +66,7 @@ function HasteDependencyResolver(options) {
|
|||
this._depGraph = new DependencyGraph({
|
||||
roots: opts.projectRoots,
|
||||
assetRoots_DEPRECATED: opts.assetRoots,
|
||||
assetExts: opts.assetExts,
|
||||
ignoreFilePath: function(filepath) {
|
||||
return filepath.indexOf('__tests__') !== -1 ||
|
||||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
|
||||
|
|
|
@ -249,6 +249,15 @@ Package.prototype._getMappings = function() {
|
|||
return mappings;
|
||||
};
|
||||
|
||||
Package.prototype.getJSModulePaths = function() {
|
||||
return this._modules.filter(function(module) {
|
||||
// Filter out non-js files. Like images etc.
|
||||
return !module.virtual;
|
||||
}).map(function(module) {
|
||||
return module.sourcePath;
|
||||
});
|
||||
};
|
||||
|
||||
Package.prototype.getDebugInfo = function() {
|
||||
return [
|
||||
'<div><h3>Main Module:</h3> ' + this._mainModuleId + '</div>',
|
||||
|
|
|
@ -204,8 +204,28 @@ describe('Package', function() {
|
|||
expect(p.getAssets()).toEqual([asset1, asset2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getJSModulePaths()', function() {
|
||||
it('should return module paths', function() {
|
||||
var p = new Package('test_url');
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;\n',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'image module;\nimage module;',
|
||||
virtual: true,
|
||||
sourceCode: 'image module;\nimage module;',
|
||||
sourcePath: 'image.png',
|
||||
}));
|
||||
|
||||
expect(p.getJSModulePaths()).toEqual(['foo path']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function genSourceMap(modules) {
|
||||
var sourceMapGen = new SourceMapGenerator({file: 'bundle.js', version: 3});
|
||||
var packageLineNo = 0;
|
||||
|
|
|
@ -86,6 +86,7 @@ function Packager(options) {
|
|||
moduleFormat: opts.moduleFormat,
|
||||
assetRoots: opts.assetRoots,
|
||||
fileWatcher: opts.fileWatcher,
|
||||
assetExts: opts.assetExts,
|
||||
});
|
||||
|
||||
this._transformer = new Transformer({
|
||||
|
|
|
@ -60,7 +60,7 @@ var validateOpts = declareOpts({
|
|||
},
|
||||
assetExts: {
|
||||
type: 'array',
|
||||
default: ['png'],
|
||||
default: ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'],
|
||||
},
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue