From 1ff22b7b9c41d66dab2342d682b11b8d358b1c46 Mon Sep 17 00:00:00 2001 From: Nathan Spaun Date: Mon, 31 Aug 2015 17:34:51 -0700 Subject: [PATCH 01/27] Adding jest tests to groups rn --- .../__mocks__/NativeModules.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js index 266a50d34..113a09262 100644 --- a/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js +++ b/Libraries/BatchedBridge/BatchedBridgedModules/__mocks__/NativeModules.js @@ -30,6 +30,12 @@ var NativeModules = { customBubblingEventTypes: {}, customDirectEventTypes: {}, Dimensions: {}, + RCTModalFullscreenView: { + Constants: {}, + }, + RCTScrollView: { + Constants: {}, + }, }, AsyncLocalStorage: { getItem: jest.genMockFunction(), @@ -44,6 +50,13 @@ var NativeModules = { appVersion: '0', buildVersion: '0', }, + ModalFullscreenViewManager: {}, + AlertManager: { + alertWithArgs: jest.genMockFunction(), + }, + Pasteboard: { + setPasteboardString: jest.genMockFunction(), + }, }; module.exports = NativeModules; From 2b3a4bd27d875b3fb7a0af1651488c7da5df48ca Mon Sep 17 00:00:00 2001 From: Ted Suzman Date: Tue, 1 Sep 2015 02:18:14 -0700 Subject: [PATCH 02/27] [ReactNative] Maintain order of bridge calls Summary: When bridge calls are made, they should be dispatched to their destination GCD queue in the same order they were made. (It looks like this invariant broke in 336e18d, which caused call order to depend on the iteration of `NSMapTable` keys whenever there are calls to multiple modules that share a queue) Fixes #1941 (in which RCTUIManager createView addUIBlock blocks were sometimes running after other blocks that depended on them) I'm a react-native/iOS/objc newbie, so please excuse any ignorance this commit may well contain :) Closes https://github.com/facebook/react-native/pull/2488 Github Author: Ted Suzman --- React/Base/RCTBatchedBridge.m | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index 5ef4c6502..3151d8005 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -664,22 +664,23 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR // verify that class has been registered (void)_modulesByName[moduleData.name]; } - NSMutableOrderedSet *set = [buckets objectForKey:moduleData]; + id queue = [moduleData queue]; + NSMutableOrderedSet *set = [buckets objectForKey:queue]; if (!set) { set = [NSMutableOrderedSet new]; - [buckets setObject:set forKey:moduleData]; + [buckets setObject:set forKey:queue]; } [set addObject:@(i)]; } - for (RCTModuleData *moduleData in buckets) { + for (id queue in buckets) { RCTProfileBeginFlowEvent(); - [moduleData dispatchBlock:^{ + dispatch_block_t block = ^{ RCTProfileEndFlowEvent(); RCTProfileBeginEvent(0, RCTCurrentThreadName(), nil); - NSOrderedSet *calls = [buckets objectForKey:moduleData]; + NSOrderedSet *calls = [buckets objectForKey:queue]; @autoreleasepool { for (NSNumber *indexObj in calls) { NSUInteger index = indexObj.unsignedIntegerValue; @@ -693,7 +694,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR RCTProfileEndEvent(0, @"objc_call,dispatch_async", @{ @"calls": @(calls.count), }); - }]; + }; + + if (queue == RCTJSThread) { + [_javaScriptExecutor executeBlockOnJavaScriptQueue:block]; + } else if (queue) { + dispatch_async(queue, block); + } } // TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case? From 853d5b2221a692110607ef12916263078141e107 Mon Sep 17 00:00:00 2001 From: James Ide Date: Tue, 1 Sep 2015 03:23:29 -0700 Subject: [PATCH 03/27] [TextInput] Set scrollsToTop = NO for UITextViews Summary: Now that UITextViews have a delegate, they consume the "tap to scroll to top" gesture. This diff restores the original behavior of letting the top-level scroll view (if any) scroll to top instead. I tried exposing scrollsToTop as a prop and was semi-successful in that I could turn scroll-to-top on and off for the top-level scroll view scroll, but the text view itself would never scroll to top. So instead of exposing it as a prop, this diff sets scrollsToTop always to NO, which is how TextInput behaved previously. Closes https://github.com/facebook/react-native/pull/2333 Github Author: James Ide --- Libraries/Text/RCTTextView.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Libraries/Text/RCTTextView.m b/Libraries/Text/RCTTextView.m index bcae5fe0a..d21360ed2 100644 --- a/Libraries/Text/RCTTextView.m +++ b/Libraries/Text/RCTTextView.m @@ -35,6 +35,7 @@ _textView = [[UITextView alloc] initWithFrame:self.bounds]; _textView.backgroundColor = [UIColor clearColor]; + _textView.scrollsToTop = NO; _textView.delegate = self; [self addSubview:_textView]; } From 5b2d8000ae189b76888b40bafef1302b08ecb9b5 Mon Sep 17 00:00:00 2001 From: Andrei Coman Date: Tue, 1 Sep 2015 05:08:25 -0700 Subject: [PATCH 05/27] [ReactNative][SyncDiff] Add option to make examples platform specific --- Examples/UIExplorer/ExampleTypes.js | 2 ++ Examples/UIExplorer/createExamplePage.js | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/Examples/UIExplorer/ExampleTypes.js b/Examples/UIExplorer/ExampleTypes.js index 44db06a6c..ac9deaadf 100644 --- a/Examples/UIExplorer/ExampleTypes.js +++ b/Examples/UIExplorer/ExampleTypes.js @@ -14,11 +14,13 @@ * @providesModule ExampleTypes * @flow */ +'use strict'; export type Example = { title: string, render: () => ?ReactElement, description?: string, + platform?: string; }; export type ExampleModule = { diff --git a/Examples/UIExplorer/createExamplePage.js b/Examples/UIExplorer/createExamplePage.js index 86525c485..410688160 100644 --- a/Examples/UIExplorer/createExamplePage.js +++ b/Examples/UIExplorer/createExamplePage.js @@ -17,6 +17,9 @@ 'use strict'; var React = require('react-native'); +var { + Platform, +} = React; var ReactNative = require('ReactNative'); var UIExplorerBlock = require('./UIExplorerBlock'); var UIExplorerPage = require('./UIExplorerPage'); @@ -36,6 +39,12 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule) }, getBlock: function(example: Example, i) { + if (example.platform) { + if (Platform.OS !== example.platform) { + return; + } + example.title += ' (' + example.platform + ' only)'; + } // Hack warning: This is a hack because the www UI explorer requires // renderComponent to be called. var originalRender = React.render; From c6240c74412027601bf70a169dcfd97f8b87e911 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 1 Sep 2015 05:19:03 -0700 Subject: [PATCH 06/27] [ReactNative] Ensure WebViewExecutor's webview is created on the main thread Summary: Fixes #2464 After the bridge parallelisation of the bridge initialisation the executors were being `setUp` in a background thread, and the `RCTWebViewExecutor` was crashing when creating a `UIWebView` out of the main thread. Wrap the `UIWebView` creation in a call to the main thread. --- React/Executors/RCTWebViewExecutor.m | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/React/Executors/RCTWebViewExecutor.m b/React/Executors/RCTWebViewExecutor.m index edc3846a0..5206cabef 100644 --- a/React/Executors/RCTWebViewExecutor.m +++ b/React/Executors/RCTWebViewExecutor.m @@ -66,13 +66,15 @@ RCT_EXPORT_MODULE() - (void)setUp { if (!_webView) { - _webView = [UIWebView new]; + [self executeBlockOnJavaScriptQueue:^{ + _webView = [UIWebView new]; + _webView.delegate = self; + }]; } _objectsToInject = [NSMutableDictionary new]; - _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL], - _scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL], - _webView.delegate = self; + _commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]*?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL]; + _scriptTagsRegex = [NSRegularExpression regularExpressionWithPattern:@"<(\\/?script[^>]*?)>" options:0 error:NULL]; } - (void)invalidate From 3bef953f77fffebb9b885567b378e8dbb18b49b1 Mon Sep 17 00:00:00 2001 From: futbalguy Date: Tue, 1 Sep 2015 05:12:48 -0700 Subject: [PATCH 07/27] Added clarifying language to error for not finding development server Summary: Added additional check to make if seeing error message on not finding development server: Node server URL is correctly set in AppDelegate. Should make it easier for a developer that is not running on localhost for the first time. Screenshot of updated error message: ![img_0015 1](https://cloud.githubusercontent.com/assets/7111607/9553286/d7081c30-4d72-11e5-8c24-e3e5c83281d0.PNG) Closes https://github.com/facebook/react-native/pull/2484 Github Author: futbalguy --- React/Base/RCTJavaScriptLoader.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/React/Base/RCTJavaScriptLoader.m b/React/Base/RCTJavaScriptLoader.m index 0aa73c68d..abc71b201 100755 --- a/React/Base/RCTJavaScriptLoader.m +++ b/React/Base/RCTJavaScriptLoader.m @@ -38,7 +38,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) // Handle general request errors if (error) { if ([error.domain isEqualToString:NSURLErrorDomain]) { - NSString *desc = [@"Could not connect to development server. Ensure node server is running and available on the same network - run 'npm start' from react-native root\n\nURL: " stringByAppendingString:scriptURL.absoluteString]; + NSString *desc = [@"Could not connect to development server.\n\nEnsure the following:\n- Node server is running and available on the same network - run 'npm start' from react-native root\n- Node server URL is correctly set in AppDelegate\n\nURL: " stringByAppendingString:scriptURL.absoluteString]; NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: desc, NSLocalizedFailureReasonErrorKey: error.localizedDescription, From c522e3b16e06e6a33f9a904657f440bc81d9d640 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 1 Sep 2015 06:58:00 -0700 Subject: [PATCH 08/27] [ReactNative] Re-enable timers test Summary: Timers test was failing but I believe that D2399150 fixes it. Re-enable and monitor. --- .../UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m index ccf5e7f74..267ed1409 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/IntegrationTests.m @@ -57,7 +57,7 @@ expectErrorRegex:@"because shouldThrow"]; } -- (void)DISABLED_testTimers // #8192477 +- (void)testTimers { [_runner runTest:_cmd module:@"TimersTest"]; } From 9cede03846932cd466bebcc4d6833a077a0942c8 Mon Sep 17 00:00:00 2001 From: Andrei Coman Date: Tue, 1 Sep 2015 14:51:46 -0100 Subject: [PATCH 09/27] [ReactNative][SyncDiff] Add Image Example --- Examples/UIExplorer/ImageExample.js | 34 ++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Examples/UIExplorer/ImageExample.js b/Examples/UIExplorer/ImageExample.js index cdf664c43..cea7b5511 100644 --- a/Examples/UIExplorer/ImageExample.js +++ b/Examples/UIExplorer/ImageExample.js @@ -97,6 +97,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Image Download Progress', @@ -105,6 +106,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Border Color', @@ -122,6 +124,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Border Width', @@ -139,6 +142,7 @@ exports.examples = [ ); }, + platform: 'ios', }, { title: 'Border Radius', @@ -146,17 +150,12 @@ exports.examples = [ return ( ); @@ -245,19 +244,19 @@ exports.examples = [ @@ -266,19 +265,19 @@ exports.examples = [ @@ -336,6 +335,7 @@ exports.examples = [ render: function() { return ; }, + platform: 'ios', }, ]; From 3cd9187a266427392a32dbdd6d787deedf46f966 Mon Sep 17 00:00:00 2001 From: Marius Butuc Date: Tue, 1 Sep 2015 08:21:35 -0700 Subject: [PATCH 10/27] Fix LinkingIOS docs typo Summary: And yes, just agreed to the Contributor License Agreement (CLA). :+1: Closes https://github.com/facebook/react-native/pull/2483 Github Author: Marius Butuc --- Libraries/LinkingIOS/LinkingIOS.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Libraries/LinkingIOS/LinkingIOS.js b/Libraries/LinkingIOS/LinkingIOS.js index 84960ff32..494091190 100644 --- a/Libraries/LinkingIOS/LinkingIOS.js +++ b/Libraries/LinkingIOS/LinkingIOS.js @@ -30,7 +30,7 @@ var DEVICE_NOTIF_EVENT = 'openURL'; * * #### Handling deep links * - * If your app was launched from a external url registered to your app you can + * If your app was launched from an external url registered to your app you can * access and handle it from any component you want with * * ``` @@ -127,7 +127,7 @@ class LinkingIOS { } /** - * Determine wether or not the an installed app can handle a given `url` + * Determine wether or not an installed app can handle a given `url` * The callback function will be called with `bool supported` as the only argument */ static canOpenURL(url: string, callback: Function) { From 0426f502bb6821b4134f5160f2d79f1a3834d951 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Tue, 1 Sep 2015 09:36:39 -0700 Subject: [PATCH 11/27] [ReactNative] Increase timeout and re-enable allocation tests Summary: Two of the allocation were disabled due to eventual failures on Travis, but haven't failed internally nor locally. I'm bumping the time out and re-enabling them to see if that was the case. --- .../UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m index cd44b7229..4fe7294fb 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTAllocationTests.m @@ -23,7 +23,7 @@ #define RUN_RUNLOOP_WHILE(CONDITION) \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wshadow\"") \ -NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:0.1]; \ +NSDate *timeout = [[NSDate date] dateByAddingTimeInterval:5]; \ while ((CONDITION) && [timeout timeIntervalSinceNow] > 0) { \ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:timeout]; \ } \ @@ -141,7 +141,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a XCTAssertNil(weakMethod, @"RCTModuleMethod should have been deallocated"); } -- (void)DISABLED_testJavaScriptExecutorIsDeallocated // flaky: #8195866 +- (void)testJavaScriptExecutorIsDeallocated { __weak id weakExecutor; @autoreleasepool { @@ -157,7 +157,7 @@ RCT_EXPORT_METHOD(test:(__unused NSString *)a XCTAssertNil(weakExecutor, @"JavaScriptExecutor should have been released"); } -- (void)disabled_testJavaScriptContextIsDeallocated +- (void)testJavaScriptContextIsDeallocated { __weak id weakContext; @autoreleasepool { From bc64f26a7a39ee364526d7e47bbd32855eca89be Mon Sep 17 00:00:00 2001 From: Andrei Coman Date: Tue, 1 Sep 2015 16:46:28 -0100 Subject: [PATCH 12/27] [ReactNative][SyncDiff] Add border example --- Examples/UIExplorer/BorderExample.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js index a9436108d..0370e69b2 100644 --- a/Examples/UIExplorer/BorderExample.js +++ b/Examples/UIExplorer/BorderExample.js @@ -26,12 +26,12 @@ var styles = StyleSheet.create({ }, border1: { borderWidth: 10, - borderColor: 'brown', + borderColor: '#a52a2a', }, borderRadius: { borderWidth: 10, borderRadius: 10, - borderColor: 'cyan', + borderColor: '#00ffff', }, border2: { borderWidth: 10, @@ -134,6 +134,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'border*Width & border*Color', + platform: 'ios', render() { return ; } @@ -141,6 +142,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'border*Width & border*Color', + platform: 'ios', render() { return ; } @@ -148,6 +150,7 @@ exports.examples = [ { title: 'Custom Borders', description: 'borderRadius & clipping', + platform: 'ios', render() { return ( From 36dfd402b2329f4f22caddd93b3e2295d02ff8c8 Mon Sep 17 00:00:00 2001 From: Hedger Wang Date: Tue, 1 Sep 2015 10:31:20 -0700 Subject: [PATCH 13/27] [madman] Supports `onLayout` for `AdsManagerTouchableHighlight`. Summary: Supports `onLayout` for Touchable*` by piping onLayout through to the native component inside since only native components support it by default. --- Libraries/Components/Touchable/TouchableHighlight.js | 1 + Libraries/Components/Touchable/TouchableOpacity.js | 1 + .../Components/Touchable/TouchableWithoutFeedback.js | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 4b6442ed4..27ddcdd4e 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -211,6 +211,7 @@ var TouchableHighlight = React.createClass({ accessible={true} ref={UNDERLAY_REF} style={this.state.underlayStyle} + onLayout={this.props.onLayout} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest} onResponderGrant={this.touchableHandleResponderGrant} diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 3d9780d8a..b5203cfc7 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -155,6 +155,7 @@ var TouchableOpacity = React.createClass({ accessible={true} style={[this.props.style, {opacity: this.state.anim}]} testID={this.props.testID} + onLayout={this.props.onLayout} onStartShouldSetResponder={this.touchableHandleStartShouldSetResponder} onResponderTerminationRequest={this.touchableHandleResponderTerminationRequest} onResponderGrant={this.touchableHandleResponderGrant} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index d8b171182..a4921052e 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -44,7 +44,15 @@ var TouchableWithoutFeedback = React.createClass({ onPress: React.PropTypes.func, onPressIn: React.PropTypes.func, onPressOut: React.PropTypes.func, + /** + * Invoked on mount and layout changes with + * + * `{nativeEvent: {layout: {x, y, width, height}}}` + */ + onLayout: React.PropTypes.func, + onLongPress: React.PropTypes.func, + /** * Delay in ms, from the start of the touch, before onPressIn is called. */ @@ -113,6 +121,7 @@ var TouchableWithoutFeedback = React.createClass({ return (React: any).cloneElement(onlyChild(this.props.children), { accessible: this.props.accessible !== false, testID: this.props.testID, + onLayout: this.props.onLayout, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, onResponderTerminationRequest: this.touchableHandleResponderTerminationRequest, onResponderGrant: this.touchableHandleResponderGrant, From 39ae5daec344311eaddba709c05689369d3d7139 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 1 Sep 2015 11:51:51 -0700 Subject: [PATCH 14/27] [react-pacakger] Max idle time for socket server is too long --- packager/react-packager/src/SocketInterface/SocketServer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packager/react-packager/src/SocketInterface/SocketServer.js b/packager/react-packager/src/SocketInterface/SocketServer.js index d3cb752d1..b292b2bfa 100644 --- a/packager/react-packager/src/SocketInterface/SocketServer.js +++ b/packager/react-packager/src/SocketInterface/SocketServer.js @@ -15,7 +15,7 @@ const debug = require('debug')('ReactPackager:SocketServer'); const fs = require('fs'); const net = require('net'); -const MAX_IDLE_TIME = 10 * 60 * 1000; +const MAX_IDLE_TIME = 30 * 1000; class SocketServer { constructor(sockPath, options) { @@ -118,7 +118,7 @@ class SocketServer { this._deathTimer = setTimeout(() => { if (this._jobs <= 0) { debug('server dying', process.pid); - process.exit(1); + process.exit(); } this._dieEventually(); }, MAX_IDLE_TIME); From b17b328aedb47c1cce65404123926ac207113eae Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Tue, 1 Sep 2015 16:40:47 -0700 Subject: [PATCH 15/27] [react-packager] Fix cache issues --- packager/react-packager/src/lib/getCacheFilePath.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packager/react-packager/src/lib/getCacheFilePath.js b/packager/react-packager/src/lib/getCacheFilePath.js index d5ef19b31..1d8585297 100644 --- a/packager/react-packager/src/lib/getCacheFilePath.js +++ b/packager/react-packager/src/lib/getCacheFilePath.js @@ -12,7 +12,7 @@ const crypto = require('crypto'); const path = require('path'); const tmpdir = require('os').tmpDir(); -function getCacheFilePath(args) { +function getCacheFilePath(...args) { args = Array.prototype.slice.call(args); const prefix = args.shift(); From 4379aa00deee71f56832bcaea0da19c47a6148d7 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Tue, 1 Sep 2015 20:33:25 -0700 Subject: [PATCH 16/27] [RN] Use default param for elastic bounciness --- Libraries/Animated/Easing.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Libraries/Animated/Easing.js b/Libraries/Animated/Easing.js index 58969278b..fe40b20b2 100644 --- a/Libraries/Animated/Easing.js +++ b/Libraries/Animated/Easing.js @@ -69,10 +69,7 @@ class Easing { * http://tiny.cc/elastic_b_1 (default bounciness = 1) * http://tiny.cc/elastic_b_3 (bounciness = 3) */ - static elastic(bounciness: number): (t: number) => number { - if (arguments.length === 0) { - bounciness = 1; - } + static elastic(bounciness: number = 1): (t: number) => number { var p = bounciness * Math.PI; return (t) => 1 - Math.pow(Math.cos(t * Math.PI / 2), 3) * Math.cos(t * p); }; From 848839858b4f4e03b83e625304af21aa8dd8ff05 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Wed, 2 Sep 2015 05:58:10 -0700 Subject: [PATCH 17/27] Added mechanism for directly mapping JS event handlers to blocks Summary: Currently, the system for mapping JS event handlers to blocks is quite clean on the JS side, but is clunky on the native side. The event property is passed as a boolean, which can then be checked by the native side, and if true, the native side is supposed to send an event via the event dispatcher. This diff adds the facility to declare the property as a block instead. This means that the event side can simply call the block, and it will automatically send the event. Because the blocks for bubbling and direct events are named differently, we can also use this to generate the event registration data and get rid of the arrays of event names. The name of the event is inferred from the property name, which means that the property for an event called "load" must be called `onLoad` or the mapping won't work. This can be optionally remapped to a different property name on the view itself if necessary, e.g. RCT_REMAP_VIEW_PROPERTY(onLoad, loadEventBlock, RCTDirectEventBlock) If you don't want to use this mechanism then for now it is still possible to declare the property as a BOOL instead and use the old mechanism (this approach is now deprecated however, and may eventually be removed altogether). --- Libraries/Components/MapView/MapView.js | 4 +- .../Components/SliderIOS/SliderIOS.ios.js | 4 +- .../Components/SwitchIOS/SwitchIOS.ios.js | 4 +- Libraries/Components/WebView/WebView.ios.js | 8 +- Libraries/Image/RCTImageView.m | 31 +-- Libraries/Image/RCTImageViewManager.m | 21 +- React/Modules/RCTUIManager.m | 144 ++++++------- React/React.xcodeproj/project.pbxproj | 6 + React/Views/RCTComponent.h | 7 + React/Views/RCTComponentData.m | 203 ++++++++++++------ React/Views/RCTDatePicker.h | 14 ++ React/Views/RCTDatePicker.m | 41 ++++ React/Views/RCTDatePickerManager.m | 19 +- React/Views/RCTMap.h | 6 +- React/Views/RCTMapManager.m | 89 ++++---- React/Views/RCTNavItem.h | 5 + React/Views/RCTNavItem.m | 52 +++-- React/Views/RCTNavItemManager.m | 3 + React/Views/RCTNavigator.m | 27 ++- React/Views/RCTNavigatorManager.m | 18 +- React/Views/RCTPicker.h | 7 - React/Views/RCTPicker.m | 38 ++-- React/Views/RCTPickerManager.m | 2 +- React/Views/RCTSegmentedControl.h | 5 +- React/Views/RCTSegmentedControl.m | 26 +-- React/Views/RCTSegmentedControlManager.m | 3 +- React/Views/RCTShadowView.h | 2 +- React/Views/RCTSlider.h | 4 + React/Views/RCTSliderManager.m | 24 +-- React/Views/RCTSwitch.h | 3 + React/Views/RCTSwitchManager.m | 9 +- React/Views/RCTTabBar.h | 4 - React/Views/RCTTabBar.m | 14 +- React/Views/RCTTabBarItem.h | 3 + React/Views/RCTTabBarItemManager.m | 9 +- React/Views/RCTTabBarManager.m | 2 +- React/Views/RCTView.h | 9 +- React/Views/RCTView.m | 8 +- React/Views/RCTViewManager.h | 10 +- React/Views/RCTViewManager.m | 34 +-- React/Views/RCTWebView.h | 4 - React/Views/RCTWebView.m | 76 +++---- React/Views/RCTWebViewManager.m | 15 +- React/Views/RCTWrapperViewController.h | 8 +- React/Views/RCTWrapperViewController.m | 29 +-- React/Views/UIView+React.m | 2 +- 46 files changed, 548 insertions(+), 508 deletions(-) create mode 100644 React/Views/RCTDatePicker.h create mode 100644 React/Views/RCTDatePicker.m diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index da4350e4c..146034f7c 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -277,7 +277,9 @@ if (Platform.OS === 'android') { uiViewClassName: 'RCTMap', }); } else { - var RCTMap = requireNativeComponent('RCTMap', MapView); + var RCTMap = requireNativeComponent('RCTMap', MapView, { + nativeOnly: {onChange: true, onPress: true} + }); } module.exports = MapView; diff --git a/Libraries/Components/SliderIOS/SliderIOS.ios.js b/Libraries/Components/SliderIOS/SliderIOS.ios.js index bfb2b9271..0c06fbf6f 100644 --- a/Libraries/Components/SliderIOS/SliderIOS.ios.js +++ b/Libraries/Components/SliderIOS/SliderIOS.ios.js @@ -107,6 +107,8 @@ var styles = StyleSheet.create({ }, }); -var RCTSlider = requireNativeComponent('RCTSlider', SliderIOS); +var RCTSlider = requireNativeComponent('RCTSlider', SliderIOS, { + nativeOnly: { onChange: true }, +}); module.exports = SliderIOS; diff --git a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js index 5a56e36b7..e99d51210 100644 --- a/Libraries/Components/SwitchIOS/SwitchIOS.ios.js +++ b/Libraries/Components/SwitchIOS/SwitchIOS.ios.js @@ -108,6 +108,8 @@ var styles = StyleSheet.create({ }, }); -var RCTSwitch = requireNativeComponent('RCTSwitch', SwitchIOS); +var RCTSwitch = requireNativeComponent('RCTSwitch', SwitchIOS, { + nativeOnly: { onChange: true } +}); module.exports = SwitchIOS; diff --git a/Libraries/Components/WebView/WebView.ios.js b/Libraries/Components/WebView/WebView.ios.js index 5bb0349cc..a3d457bfd 100644 --- a/Libraries/Components/WebView/WebView.ios.js +++ b/Libraries/Components/WebView/WebView.ios.js @@ -226,7 +226,13 @@ var WebView = React.createClass({ }, }); -var RCTWebView = requireNativeComponent('RCTWebView', WebView); +var RCTWebView = requireNativeComponent('RCTWebView', WebView, { + nativeOnly: { + onLoadingStart: true, + onLoadingError: true, + onLoadingFinish: true, + }, +}); var styles = StyleSheet.create({ container: { diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index ddbd23d77..db67ed44a 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -21,11 +21,11 @@ @interface RCTImageView () -@property (nonatomic, assign) BOOL onLoadStart; -@property (nonatomic, assign) BOOL onProgress; -@property (nonatomic, assign) BOOL onError; -@property (nonatomic, assign) BOOL onLoad; -@property (nonatomic, assign) BOOL onLoadEnd; +@property (nonatomic, copy) RCTDirectEventBlock onLoadStart; +@property (nonatomic, copy) RCTDirectEventBlock onProgress; +@property (nonatomic, copy) RCTDirectEventBlock onError; +@property (nonatomic, copy) RCTDirectEventBlock onLoad; +@property (nonatomic, copy) RCTDirectEventBlock onLoadEnd; @end @@ -116,19 +116,16 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) { if (_onLoadStart) { - NSDictionary *event = @{ @"target": self.reactTag }; - [_bridge.eventDispatcher sendInputEventWithName:@"loadStart" body:event]; + _onLoadStart(nil); } RCTImageLoaderProgressBlock progressHandler = nil; if (_onProgress) { progressHandler = ^(int64_t loaded, int64_t total) { - NSDictionary *event = @{ - @"target": self.reactTag, + _onProgress(@{ @"loaded": @((double)loaded), @"total": @((double)total), - }; - [_bridge.eventDispatcher sendInputEventWithName:@"progress" body:event]; + }); }; } @@ -147,21 +144,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } if (error) { if (_onError) { - NSDictionary *event = @{ - @"target": self.reactTag, - @"error": error.localizedDescription, - }; - [_bridge.eventDispatcher sendInputEventWithName:@"error" body:event]; + _onError(@{ @"error": error.localizedDescription }); } } else { if (_onLoad) { - NSDictionary *event = @{ @"target": self.reactTag }; - [_bridge.eventDispatcher sendInputEventWithName:@"load" body:event]; + _onLoad(nil); } } if (_onLoadEnd) { - NSDictionary *event = @{ @"target": self.reactTag }; - [_bridge.eventDispatcher sendInputEventWithName:@"loadEnd" body:event]; + _onLoadEnd(nil); } }]; } else { diff --git a/Libraries/Image/RCTImageViewManager.m b/Libraries/Image/RCTImageViewManager.m index c797cdfab..bcf2f0bdb 100644 --- a/Libraries/Image/RCTImageViewManager.m +++ b/Libraries/Image/RCTImageViewManager.m @@ -27,11 +27,11 @@ RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets) RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage) RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode) RCT_EXPORT_VIEW_PROPERTY(src, NSString) -RCT_EXPORT_VIEW_PROPERTY(onLoadStart, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onProgress, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onError, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onLoad, BOOL) -RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, BOOL) +RCT_EXPORT_VIEW_PROPERTY(onLoadStart, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onLoad, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onLoadEnd, RCTDirectEventBlock) RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView) { if (json) { @@ -43,15 +43,4 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView) } } -- (NSArray *)customDirectEventTypes -{ - return @[ - @"loadStart", - @"progress", - @"error", - @"load", - @"loadEnd", - ]; -} - @end diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 9af3a6ceb..4969b0055 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -455,6 +455,9 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); [frames addObject:[NSValue valueWithCGRect:shadowView.frame]]; [areNew addObject:@(shadowView.isNewView)]; [parentsAreNew addObject:@(shadowView.superview.isNewView)]; + + // TODO (#8214142): this can be greatly simplified by sending the layout + // event directly from the shadow thread, which may be better anyway. id event = (id)kCFNull; if (shadowView.onLayout) { event = @{ @@ -1128,67 +1131,68 @@ RCT_EXPORT_METHOD(clearJSResponder) }]; } -- (NSDictionary *)bubblingEventsConfig -{ - NSMutableDictionary *customBubblingEventTypesConfigs = [NSMutableDictionary new]; - for (RCTComponentData *componentData in _componentDataByName.allValues) { - RCTViewManager *manager = componentData.manager; - if (RCTClassOverridesInstanceMethod([manager class], @selector(customBubblingEventTypes))) { - NSArray *events = [manager customBubblingEventTypes]; - if (RCT_DEBUG) { - RCTAssert(!events || [events isKindOfClass:[NSArray class]], - @"customBubblingEventTypes must return an array, but %@ returned %@", - [manager class], [events class]); - } - for (NSString *eventName in events) { - NSString *topName = RCTNormalizeInputEventName(eventName); - if (!customBubblingEventTypesConfigs[topName]) { - NSString *bubbleName = [topName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"]; - customBubblingEventTypesConfigs[topName] = @{ - @"phasedRegistrationNames": @{ - @"bubbled": bubbleName, - @"captured": [bubbleName stringByAppendingString:@"Capture"], - } - }; - } - } - } - }; - - return customBubblingEventTypesConfigs; -} - -- (NSDictionary *)directEventsConfig -{ - NSMutableDictionary *customDirectEventTypes = [NSMutableDictionary new]; - for (RCTComponentData *componentData in _componentDataByName.allValues) { - RCTViewManager *manager = componentData.manager; - if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) { - NSArray *events = [manager customDirectEventTypes]; - if (RCT_DEBUG) { - RCTAssert(!events || [events isKindOfClass:[NSArray class]], - @"customDirectEventTypes must return an array, but %@ returned %@", - [manager class], [events class]); - } - for (NSString *eventName in events) { - NSString *topName = RCTNormalizeInputEventName(eventName); - if (!customDirectEventTypes[topName]) { - customDirectEventTypes[topName] = @{ - @"registrationName": [topName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"], - }; - } - } - } - }; - - return customDirectEventTypes; -} - - (NSDictionary *)constantsToExport { - NSMutableDictionary *allJSConstants = [@{ - @"customBubblingEventTypes": [self bubblingEventsConfig], - @"customDirectEventTypes": [self directEventsConfig], + NSMutableDictionary *allJSConstants = [NSMutableDictionary new]; + NSMutableDictionary *directEvents = [NSMutableDictionary new]; + NSMutableDictionary *bubblingEvents = [NSMutableDictionary new]; + + [_componentDataByName enumerateKeysAndObjectsUsingBlock: + ^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) { + + RCTViewManager *manager = componentData.manager; + NSMutableDictionary *constantsNamespace = + [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; + + // Add custom constants + // TODO: should these be inherited? + NSDictionary *constants = RCTClassOverridesInstanceMethod([manager class], @selector(constantsToExport)) ? [manager constantsToExport] : nil; + if (constants.count) { + RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name); + // add an additional 'Constants' namespace for each class + constantsNamespace[@"Constants"] = constants; + } + + // Add native props + NSDictionary *viewConfig = [componentData viewConfig]; + constantsNamespace[@"NativeProps"] = viewConfig[@"propTypes"]; + + // Add direct events + for (NSString *eventName in viewConfig[@"directEvents"]) { + if (!directEvents[eventName]) { + directEvents[eventName] = @{ + @"registrationName": [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"], + }; + } + if (RCT_DEBUG && bubblingEvents[eventName]) { + RCTLogError(@"Component '%@' re-registered bubbling event '%@' as a " + "direct event", componentData.name, eventName); + } + } + + // Add bubbling events + for (NSString *eventName in viewConfig[@"bubblingEvents"]) { + if (!bubblingEvents[eventName]) { + NSString *bubbleName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"]; + bubblingEvents[eventName] = @{ + @"phasedRegistrationNames": @{ + @"bubbled": bubbleName, + @"captured": [bubbleName stringByAppendingString:@"Capture"], + } + }; + } + if (RCT_DEBUG && directEvents[eventName]) { + RCTLogError(@"Component '%@' re-registered direct event '%@' as a " + "bubbling event", componentData.name, eventName); + } + } + + allJSConstants[name] = [constantsNamespace copy]; + }]; + + [allJSConstants addEntriesFromDictionary:@{ + @"customBubblingEventTypes": bubblingEvents, + @"customDirectEventTypes": directEvents, @"Dimensions": @{ @"window": @{ @"width": @(RCTScreenSize().width), @@ -1200,28 +1204,8 @@ RCT_EXPORT_METHOD(clearJSResponder) @"height": @(RCTScreenSize().height), }, }, - } mutableCopy]; - - [_componentDataByName enumerateKeysAndObjectsUsingBlock: - ^(NSString *name, RCTComponentData *componentData, __unused BOOL *stop) { - RCTViewManager *manager = componentData.manager; - NSMutableDictionary *constantsNamespace = - [NSMutableDictionary dictionaryWithDictionary:allJSConstants[name]]; - - // Add custom constants - // TODO: should these be inherited? - NSDictionary *constants = RCTClassOverridesInstanceMethod([manager class], @selector(constantsToExport)) ? [manager constantsToExport] : nil; - if (constants.count) { - RCTAssert(constantsNamespace[@"Constants"] == nil , @"Cannot redefine Constants in namespace: %@", name); - // add an additional 'Constants' namespace for each class - constantsNamespace[@"Constants"] = constants; - } - - // Add native props - constantsNamespace[@"NativeProps"] = [componentData viewConfig]; - - allJSConstants[name] = [constantsNamespace copy]; }]; + return allJSConstants; } diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index 97a278353..ce803446d 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */ = {isa = PBXBuildFile; fileRef = 000E6CEA1AB0E980000CDF4D /* RCTSourceCode.m */; }; 131B6AF41AF1093D00FFC3E0 /* RCTSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */; }; 131B6AF51AF1093D00FFC3E0 /* RCTSegmentedControlManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */; }; + 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */ = {isa = PBXBuildFile; fileRef = 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */; }; 13456E931ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */; }; 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */; }; 134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */; }; @@ -104,6 +105,8 @@ 131B6AF11AF1093D00FFC3E0 /* RCTSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControl.m; sourceTree = ""; }; 131B6AF21AF1093D00FFC3E0 /* RCTSegmentedControlManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSegmentedControlManager.h; sourceTree = ""; }; 131B6AF31AF1093D00FFC3E0 /* RCTSegmentedControlManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSegmentedControlManager.m; sourceTree = ""; }; + 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePicker.h; sourceTree = ""; }; + 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePicker.m; sourceTree = ""; }; 13442BF21AA90E0B0037E5B0 /* RCTAnimationType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationType.h; sourceTree = ""; }; 13442BF31AA90E0B0037E5B0 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = ""; }; 13442BF41AA90E0B0037E5B0 /* RCTViewControllerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTViewControllerProtocol.h; sourceTree = ""; }; @@ -345,6 +348,8 @@ 13456E921ADAD2DE009F94A7 /* RCTConvert+CoreLocation.m */, 13456E941ADAD482009F94A7 /* RCTConvert+MapKit.h */, 13456E951ADAD482009F94A7 /* RCTConvert+MapKit.m */, + 133CAE8C1B8E5CFD00F6AD92 /* RCTDatePicker.h */, + 133CAE8D1B8E5CFD00F6AD92 /* RCTDatePicker.m */, 58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */, 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */, 14435CE11AAC4AE100FC20F4 /* RCTMap.h */, @@ -597,6 +602,7 @@ 13456E961ADAD482009F94A7 /* RCTConvert+MapKit.m in Sources */, 13723B501A82FD3C00F88898 /* RCTStatusBarManager.m in Sources */, 000E6CEB1AB0E980000CDF4D /* RCTSourceCode.m in Sources */, + 133CAE8E1B8E5CFD00F6AD92 /* RCTDatePicker.m in Sources */, 14C2CA761B3AC64F00E6CBB2 /* RCTFrameUpdate.m in Sources */, 13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */, 83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */, diff --git a/React/Views/RCTComponent.h b/React/Views/RCTComponent.h index bc73ac8d2..215acb1d0 100644 --- a/React/Views/RCTComponent.h +++ b/React/Views/RCTComponent.h @@ -9,6 +9,13 @@ #import +/** + * These block types can be used for mapping input event handlers from JS to view + * properties. Unlike JS method callbacks, these can be called multiple times. + */ +typedef void (^RCTDirectEventBlock)(NSDictionary *body); +typedef void (^RCTBubblingEventBlock)(NSDictionary *body); + /** * Logical node in a tree of application components. Both `ShadowView` and * `UIView` conforms to this. Allows us to write utilities that reason about diff --git a/React/Views/RCTComponentData.m b/React/Views/RCTComponentData.m index b01b0268e..c6fd94127 100644 --- a/React/Views/RCTComponentData.m +++ b/React/Views/RCTComponentData.m @@ -13,6 +13,7 @@ #import "RCTBridge.h" #import "RCTShadowView.h" +#import "RCTUtils.h" #import "RCTViewManager.h" typedef void (^RCTPropBlock)(id view, id json); @@ -140,77 +141,95 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) // Build setter block void (^setterBlock)(id target, id source, id json) = nil; - NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type]; - switch (typeSignature.methodReturnType[0]) { + if (type == NSSelectorFromString(@"RCTBubblingEventBlock:") || + type == NSSelectorFromString(@"RCTDirectEventBlock:")) { -#define RCT_CASE(_value, _type) \ - case _value: { \ - _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ - _type (*get)(id, SEL) = (typeof(get))objc_msgSend; \ - void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ - setterBlock = ^(id target, id source, id json) { \ - set(target, setter, json ? convert([RCTConvert class], type, json) : get(source, getter)); \ - }; \ - break; \ - } + // Special case for event handlers + __weak RCTViewManager *weakManager = _manager; + setterBlock = ^(id target, __unused id source, id json) { + __weak id weakTarget = target; + ((void (*)(id, SEL, id))objc_msgSend)(target, setter, [RCTConvert BOOL:json] ? ^(NSDictionary *body) { + body = [NSMutableDictionary dictionaryWithDictionary:body]; + ((NSMutableDictionary *)body)[@"target"] = weakTarget.reactTag; + [weakManager.bridge.eventDispatcher sendInputEventWithName:RCTNormalizeInputEventName(name) body:body]; + } : nil); + }; - RCT_CASE(_C_SEL, SEL) - RCT_CASE(_C_CHARPTR, const char *) - RCT_CASE(_C_CHR, char) - RCT_CASE(_C_UCHR, unsigned char) - RCT_CASE(_C_SHT, short) - RCT_CASE(_C_USHT, unsigned short) - RCT_CASE(_C_INT, int) - RCT_CASE(_C_UINT, unsigned int) - RCT_CASE(_C_LNG, long) - RCT_CASE(_C_ULNG, unsigned long) - RCT_CASE(_C_LNG_LNG, long long) - RCT_CASE(_C_ULNG_LNG, unsigned long long) - RCT_CASE(_C_FLT, float) - RCT_CASE(_C_DBL, double) - RCT_CASE(_C_BOOL, BOOL) - RCT_CASE(_C_PTR, void *) - RCT_CASE(_C_ID, id) + } else { - case _C_STRUCT_B: - default: { + // Ordinary property handlers + NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type]; + switch (typeSignature.methodReturnType[0]) { - NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; - typeInvocation.selector = type; - typeInvocation.target = [RCTConvert class]; + #define RCT_CASE(_value, _type) \ + case _value: { \ + _type (*convert)(id, SEL, id) = (typeof(convert))objc_msgSend; \ + _type (*get)(id, SEL) = (typeof(get))objc_msgSend; \ + void (*set)(id, SEL, _type) = (typeof(set))objc_msgSend; \ + setterBlock = ^(id target, id source, id json) { \ + set(target, setter, json ? convert([RCTConvert class], type, json) : get(source, getter)); \ + }; \ + break; \ + } - __block NSInvocation *sourceInvocation = nil; - __block NSInvocation *targetInvocation = nil; + RCT_CASE(_C_SEL, SEL) + RCT_CASE(_C_CHARPTR, const char *) + RCT_CASE(_C_CHR, char) + RCT_CASE(_C_UCHR, unsigned char) + RCT_CASE(_C_SHT, short) + RCT_CASE(_C_USHT, unsigned short) + RCT_CASE(_C_INT, int) + RCT_CASE(_C_UINT, unsigned int) + RCT_CASE(_C_LNG, long) + RCT_CASE(_C_ULNG, unsigned long) + RCT_CASE(_C_LNG_LNG, long long) + RCT_CASE(_C_ULNG_LNG, unsigned long long) + RCT_CASE(_C_FLT, float) + RCT_CASE(_C_DBL, double) + RCT_CASE(_C_BOOL, BOOL) + RCT_CASE(_C_PTR, void *) + RCT_CASE(_C_ID, id) - setterBlock = ^(id target, id source, id json) { \ + case _C_STRUCT_B: + default: { - // Get value - void *value = malloc(typeSignature.methodReturnLength); - if (json) { - [typeInvocation setArgument:&json atIndex:2]; - [typeInvocation invoke]; - [typeInvocation getReturnValue:value]; - } else { - if (!sourceInvocation && source) { - NSMethodSignature *signature = [source methodSignatureForSelector:getter]; - sourceInvocation = [NSInvocation invocationWithMethodSignature:signature]; - sourceInvocation.selector = getter; + NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature]; + typeInvocation.selector = type; + typeInvocation.target = [RCTConvert class]; + + __block NSInvocation *sourceInvocation = nil; + __block NSInvocation *targetInvocation = nil; + + setterBlock = ^(id target, id source, id json) { \ + + // Get value + void *value = malloc(typeSignature.methodReturnLength); + if (json) { + [typeInvocation setArgument:&json atIndex:2]; + [typeInvocation invoke]; + [typeInvocation getReturnValue:value]; + } else { + if (!sourceInvocation && source) { + NSMethodSignature *signature = [source methodSignatureForSelector:getter]; + sourceInvocation = [NSInvocation invocationWithMethodSignature:signature]; + sourceInvocation.selector = getter; + } + [sourceInvocation invokeWithTarget:source]; + [sourceInvocation getReturnValue:value]; } - [sourceInvocation invokeWithTarget:source]; - [sourceInvocation getReturnValue:value]; - } - // Set value - if (!targetInvocation && target) { - NSMethodSignature *signature = [target methodSignatureForSelector:setter]; - targetInvocation = [NSInvocation invocationWithMethodSignature:signature]; - targetInvocation.selector = setter; - } - [targetInvocation setArgument:value atIndex:2]; - [targetInvocation invokeWithTarget:target]; - free(value); - }; - break; + // Set value + if (!targetInvocation && target) { + NSMethodSignature *signature = [target methodSignatureForSelector:setter]; + targetInvocation = [NSInvocation invocationWithMethodSignature:signature]; + targetInvocation.selector = setter; + } + [targetInvocation setArgument:value atIndex:2]; + [targetInvocation invokeWithTarget:target]; + free(value); + }; + break; + } } } @@ -292,9 +311,35 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) - (NSDictionary *)viewConfig { Class managerClass = [_manager class]; - NSMutableDictionary *propTypes = [NSMutableDictionary new]; + + NSMutableArray *directEvents = [NSMutableArray new]; + if (RCTClassOverridesInstanceMethod(managerClass, @selector(customDirectEventTypes))) { + NSArray *events = [_manager customDirectEventTypes]; + if (RCT_DEBUG) { + RCTAssert(!events || [events isKindOfClass:[NSArray class]], + @"customDirectEventTypes must return an array, but %@ returned %@", + managerClass, [events class]); + } + for (NSString *event in events) { + [directEvents addObject:RCTNormalizeInputEventName(event)]; + } + } + + NSMutableArray *bubblingEvents = [NSMutableArray new]; + if (RCTClassOverridesInstanceMethod(managerClass, @selector(customBubblingEventTypes))) { + NSArray *events = [_manager customBubblingEventTypes]; + if (RCT_DEBUG) { + RCTAssert(!events || [events isKindOfClass:[NSArray class]], + @"customBubblingEventTypes must return an array, but %@ returned %@", + managerClass, [events class]); + } + for (NSString *event in events) { + [bubblingEvents addObject:RCTNormalizeInputEventName(event)]; + } + } unsigned int count = 0; + NSMutableDictionary *propTypes = [NSMutableDictionary new]; Method *methods = class_copyMethodList(object_getClass(managerClass), &count); for (unsigned int i = 0; i < count; i++) { Method method = methods[i]; @@ -309,13 +354,41 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) RCTLogError(@"Property '%@' of component '%@' redefined from '%@' " "to '%@'", name, _name, propTypes[name], type); } - propTypes[name] = type; + + if ([type isEqualToString:@"RCTBubblingEventBlock"]) { + [bubblingEvents addObject:RCTNormalizeInputEventName(name)]; + propTypes[name] = @"BOOL"; + } else if ([type isEqualToString:@"RCTDirectEventBlock"]) { + [directEvents addObject:RCTNormalizeInputEventName(name)]; + propTypes[name] = @"BOOL"; + } else { + propTypes[name] = type; + } } } } free(methods); - return propTypes; + if (RCT_DEBUG) { + for (NSString *event in directEvents) { + if ([bubblingEvents containsObject:event]) { + RCTLogError(@"Component '%@' registered '%@' as both a bubbling event " + "and a direct event", _name, event); + } + } + for (NSString *event in bubblingEvents) { + if ([directEvents containsObject:event]) { + RCTLogError(@"Component '%@' registered '%@' as both a bubbling event " + "and a direct event", _name, event); + } + } + } + + return @{ + @"propTypes" : propTypes, + @"directEvents" : directEvents, + @"bubblingEvents" : bubblingEvents, + }; } @end diff --git a/React/Views/RCTDatePicker.h b/React/Views/RCTDatePicker.h new file mode 100644 index 000000000..f036651b4 --- /dev/null +++ b/React/Views/RCTDatePicker.h @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface RCTDatePicker : UIDatePicker + +@end diff --git a/React/Views/RCTDatePicker.m b/React/Views/RCTDatePicker.m new file mode 100644 index 000000000..0e392dcec --- /dev/null +++ b/React/Views/RCTDatePicker.m @@ -0,0 +1,41 @@ +/** + * 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 "RCTDatePicker.h" + +#import "RCTUtils.h" +#import "UIView+React.h" + +@interface RCTDatePicker () + +@property (nonatomic, copy) RCTBubblingEventBlock onChange; + +@end + +@implementation RCTDatePicker + +- (instancetype)initWithFrame:(CGRect)frame +{ + if ((self = [super initWithFrame:frame])) { + [self addTarget:self action:@selector(didChange) + forControlEvents:UIControlEventValueChanged]; + } + return self; +} + +RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) + +- (void)didChange +{ + if (_onChange) { + _onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0) }); + } +} + +@end diff --git a/React/Views/RCTDatePickerManager.m b/React/Views/RCTDatePickerManager.m index f2bef39e3..ef9515e5a 100644 --- a/React/Views/RCTDatePickerManager.m +++ b/React/Views/RCTDatePickerManager.m @@ -10,6 +10,7 @@ #import "RCTDatePickerManager.h" #import "RCTBridge.h" +#import "RCTDatePicker.h" #import "RCTEventDispatcher.h" #import "UIView+React.h" @@ -30,14 +31,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - // TODO: we crash here if the RCTDatePickerManager is released - // while the UIDatePicker is still sending onChange events. To - // fix this we should maybe subclass UIDatePicker and make it - // be its own event target. - UIDatePicker *picker = [UIDatePicker new]; - [picker addTarget:self action:@selector(onChange:) - forControlEvents:UIControlEventValueChanged]; - return picker; + return [RCTDatePicker new]; } RCT_EXPORT_VIEW_PROPERTY(date, NSDate) @@ -47,15 +41,6 @@ RCT_EXPORT_VIEW_PROPERTY(minuteInterval, NSInteger) RCT_REMAP_VIEW_PROPERTY(mode, datePickerMode, UIDatePickerMode) RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone) -- (void)onChange:(UIDatePicker *)sender -{ - NSDictionary *event = @{ - @"target": sender.reactTag, - @"timestamp": @(sender.date.timeIntervalSince1970 * 1000.0) - }; - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event]; -} - - (NSDictionary *)constantsToExport { UIDatePicker *view = [UIDatePicker new]; diff --git a/React/Views/RCTMap.h b/React/Views/RCTMap.h index 41cc13a12..65e6aec94 100644 --- a/React/Views/RCTMap.h +++ b/React/Views/RCTMap.h @@ -11,13 +11,12 @@ #import #import "RCTConvert+MapKit.h" +#import "RCTComponent.h" extern const CLLocationDegrees RCTMapDefaultSpan; extern const NSTimeInterval RCTMapRegionChangeObserveInterval; extern const CGFloat RCTMapZoomBoundBuffer; -@class RCTEventDispatcher; - @interface RCTMap: MKMapView @property (nonatomic, assign) BOOL followUserLocation; @@ -28,6 +27,9 @@ extern const CGFloat RCTMapZoomBoundBuffer; @property (nonatomic, strong) NSTimer *regionChangeObserveTimer; @property (nonatomic, strong) NSMutableArray *annotationIds; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; +@property (nonatomic, copy) RCTBubblingEventBlock onPress; + - (void)setAnnotations:(RCTPointAnnotationArray *)annotations; @end diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m index 130446df0..f4ee06f98 100644 --- a/React/Views/RCTMapManager.m +++ b/React/Views/RCTMapManager.m @@ -46,6 +46,8 @@ RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat) RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets) RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType) RCT_EXPORT_VIEW_PROPERTY(annotations, RCTPointAnnotationArray) +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) { [view setRegion:json ? [RCTConvert MKCoordinateRegion:json] : defaultView.region animated:YES]; @@ -53,35 +55,27 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) #pragma mark MKMapViewDelegate - - -- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view +- (void)mapView:(RCTMap *)mapView didSelectAnnotationView:(MKAnnotationView *)view { - if (![view.annotation isKindOfClass:[MKUserLocation class]]) { + if (mapView.onPress && [view.annotation isKindOfClass:[RCTPointAnnotation class]]) { RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation; - NSString *title = view.annotation.title ?: @""; - NSString *subtitle = view.annotation.subtitle ?: @""; - - NSDictionary *event = @{ - @"target": mapView.reactTag, - @"action": @"annotation-click", - @"annotation": @{ - @"id": annotation.identifier, - @"title": title, - @"subtitle": subtitle, - @"latitude": @(annotation.coordinate.latitude), - @"longitude": @(annotation.coordinate.longitude) - } - }; - - [self.bridge.eventDispatcher sendInputEventWithName:@"press" body:event]; + mapView.onPress(@{ + @"action": @"annotation-click", + @"annotation": @{ + @"id": annotation.identifier, + @"title": annotation.title ?: @"", + @"subtitle": annotation.subtitle ?: @"", + @"latitude": @(annotation.coordinate.latitude), + @"longitude": @(annotation.coordinate.longitude) + } + }); } } - (MKAnnotationView *)mapView:(__unused MKMapView *)mapView viewForAnnotation:(RCTPointAnnotation *)annotation { - if ([annotation isKindOfClass:[MKUserLocation class]]) { + if (![annotation isKindOfClass:[RCTPointAnnotation class]]) { return nil; } @@ -103,23 +97,20 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) return annotationView; } -- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control +- (void)mapView:(RCTMap *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { - // Pass to js - RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation; - NSString *side = (control == view.leftCalloutAccessoryView) ? @"left" : @"right"; + if (mapView.onPress) { - NSDictionary *event = @{ - @"target": mapView.reactTag, - @"side": side, + // Pass to js + RCTPointAnnotation *annotation = (RCTPointAnnotation *)view.annotation; + mapView.onPress(@{ + @"side": (control == view.leftCalloutAccessoryView) ? @"left" : @"right", @"action": @"callout-click", @"annotationId": annotation.identifier - }; - - [self.bridge.eventDispatcher sendInputEventWithName:@"press" body:event]; + }); + } } - - (void)mapView:(RCTMap *)mapView didUpdateUserLocation:(MKUserLocation *)location { if (mapView.followUserLocation) { @@ -205,24 +196,24 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) - (void)_emitRegionChangeEvent:(RCTMap *)mapView continuous:(BOOL)continuous { - MKCoordinateRegion region = mapView.region; - if (!CLLocationCoordinate2DIsValid(region.center)) { - return; - } - -#define FLUSH_NAN(value) (isnan(value) ? 0 : value) - - NSDictionary *event = @{ - @"target": mapView.reactTag, - @"continuous": @(continuous), - @"region": @{ - @"latitude": @(FLUSH_NAN(region.center.latitude)), - @"longitude": @(FLUSH_NAN(region.center.longitude)), - @"latitudeDelta": @(FLUSH_NAN(region.span.latitudeDelta)), - @"longitudeDelta": @(FLUSH_NAN(region.span.longitudeDelta)), + if (mapView.onChange) { + MKCoordinateRegion region = mapView.region; + if (!CLLocationCoordinate2DIsValid(region.center)) { + return; } - }; - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event]; + + #define FLUSH_NAN(value) (isnan(value) ? 0 : value) + + mapView.onChange(@{ + @"continuous": @(continuous), + @"region": @{ + @"latitude": @(FLUSH_NAN(region.center.latitude)), + @"longitude": @(FLUSH_NAN(region.center.longitude)), + @"latitudeDelta": @(FLUSH_NAN(region.span.latitudeDelta)), + @"longitudeDelta": @(FLUSH_NAN(region.span.longitudeDelta)), + } + }); + } } @end diff --git a/React/Views/RCTNavItem.h b/React/Views/RCTNavItem.h index 9b75673c3..418d5c5c9 100644 --- a/React/Views/RCTNavItem.h +++ b/React/Views/RCTNavItem.h @@ -9,6 +9,8 @@ #import +#import "RCTComponent.h" + @interface RCTNavItem : UIView @property (nonatomic, copy) NSString *title; @@ -29,4 +31,7 @@ @property (nonatomic, readonly) UIBarButtonItem *leftButtonItem; @property (nonatomic, readonly) UIBarButtonItem *rightButtonItem; +@property (nonatomic, copy) RCTBubblingEventBlock onNavLeftButtonTap; +@property (nonatomic, copy) RCTBubblingEventBlock onNavRightButtonTap; + @end diff --git a/React/Views/RCTNavItem.m b/React/Views/RCTNavItem.m index f875e6aa5..5e4043f84 100644 --- a/React/Views/RCTNavItem.m +++ b/React/Views/RCTNavItem.m @@ -63,15 +63,18 @@ { if (!_leftButtonItem) { if (_leftButtonIcon) { - _leftButtonItem = [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _leftButtonItem = + [[UIBarButtonItem alloc] initWithImage:_leftButtonIcon + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavLeftButtonTapped)]; + } else if (_leftButtonTitle.length) { - _leftButtonItem = [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _leftButtonItem = + [[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavLeftButtonTapped)]; } else { _leftButtonItem = nil; } @@ -79,6 +82,13 @@ return _leftButtonItem; } +- (void)handleNavLeftButtonTapped +{ + if (_onNavLeftButtonTap) { + _onNavLeftButtonTap(nil); + } +} + - (void)setRightButtonTitle:(NSString *)rightButtonTitle { _rightButtonTitle = rightButtonTitle; @@ -95,15 +105,18 @@ { if (!_rightButtonItem) { if (_rightButtonIcon) { - _rightButtonItem = [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _rightButtonItem = + [[UIBarButtonItem alloc] initWithImage:_rightButtonIcon + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavRightButtonTapped)]; + } else if (_rightButtonTitle.length) { - _rightButtonItem = [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle - style:UIBarButtonItemStylePlain - target:nil - action:nil]; + _rightButtonItem = + [[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle + style:UIBarButtonItemStylePlain + target:self + action:@selector(handleNavRightButtonTapped)]; } else { _rightButtonItem = nil; } @@ -111,4 +124,11 @@ return _rightButtonItem; } +- (void)handleNavRightButtonTapped +{ + if (_onNavRightButtonTap) { + _onNavRightButtonTap(nil); + } +} + @end diff --git a/React/Views/RCTNavItemManager.m b/React/Views/RCTNavItemManager.m index f785934af..040b7ffce 100644 --- a/React/Views/RCTNavItemManager.m +++ b/React/Views/RCTNavItemManager.m @@ -39,4 +39,7 @@ RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage) RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage) RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString) +RCT_EXPORT_VIEW_PROPERTY(onNavLeftButtonTap, RCTBubblingEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onNavRightButtonTap, RCTBubblingEventBlock) + @end diff --git a/React/Views/RCTNavigator.m b/React/Views/RCTNavigator.m index fcbc14225..a79fe0992 100644 --- a/React/Views/RCTNavigator.m +++ b/React/Views/RCTNavigator.m @@ -193,6 +193,9 @@ NSInteger kNeverProgressed = -10000; @interface RCTNavigator() +@property (nonatomic, copy) RCTDirectEventBlock onNavigationProgress; +@property (nonatomic, copy) RCTBubblingEventBlock onNavigationComplete; + @property (nonatomic, assign) NSInteger previousRequestedTopOfStack; // Previous views are only mainted in order to detect incorrect @@ -308,12 +311,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) return; } _mostRecentProgress = nextProgress; - [_bridge.eventDispatcher sendInputEventWithName:@"navigationProgress" body:@{ - @"fromIndex": @(_currentlyTransitioningFrom), - @"toIndex": @(_currentlyTransitioningTo), - @"progress": @(nextProgress), - @"target": self.reactTag - }]; + if (_onNavigationProgress) { + _onNavigationProgress(@{ + @"fromIndex": @(_currentlyTransitioningFrom), + @"toIndex": @(_currentlyTransitioningTo), + @"progress": @(nextProgress), + }); + } } } @@ -416,10 +420,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)handleTopOfStackChanged { - [_bridge.eventDispatcher sendInputEventWithName:@"navigationComplete" body:@{ - @"target":self.reactTag, - @"stackLength":@(_navigationController.viewControllers.count) - }]; + if (_onNavigationComplete) { + _onNavigationComplete(@{ + @"stackLength":@(_navigationController.viewControllers.count) + }); + } } - (void)dispatchFakeScrollEvent @@ -502,7 +507,7 @@ BOOL jsGettingtooSlow = if (jsGettingAhead) { if (reactPushOne) { UIView *lastView = _currentViews.lastObject; - RCTWrapperViewController *vc = [[RCTWrapperViewController alloc] initWithNavItem:(RCTNavItem *)lastView eventDispatcher:_bridge.eventDispatcher]; + RCTWrapperViewController *vc = [[RCTWrapperViewController alloc] initWithNavItem:(RCTNavItem *)lastView]; vc.navigationListener = self; _numberOfViewControllerMovesToIgnore = 1; [_navigationController pushViewController:vc animated:(currentReactCount > 1)]; diff --git a/React/Views/RCTNavigatorManager.m b/React/Views/RCTNavigatorManager.m index 2627d0f64..0e900a14c 100644 --- a/React/Views/RCTNavigatorManager.m +++ b/React/Views/RCTNavigatorManager.m @@ -25,22 +25,8 @@ RCT_EXPORT_MODULE() } RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger) - -- (NSArray *)customBubblingEventTypes -{ - return @[ - @"navigationComplete", - @"navLeftButtonTap", - @"navRightButtonTap", - ]; -} - -- (NSArray *)customDirectEventTypes -{ - return @[ - @"navigationProgress", - ]; -} +RCT_EXPORT_VIEW_PROPERTY(onNavigationProgress, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onNavigationComplete, RCTBubblingEventBlock) // TODO: remove error callbacks RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(nonnull NSNumber *)reactTag diff --git a/React/Views/RCTPicker.h b/React/Views/RCTPicker.h index 9f6a486de..704fe7587 100644 --- a/React/Views/RCTPicker.h +++ b/React/Views/RCTPicker.h @@ -9,13 +9,6 @@ #import -@class RCTEventDispatcher; - @interface RCTPicker : UIPickerView -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - -@property (nonatomic, copy) NSArray *items; -@property (nonatomic, assign) NSInteger selectedIndex; - @end diff --git a/React/Views/RCTPicker.m b/React/Views/RCTPicker.m index 09c7247d2..1f256cc48 100644 --- a/React/Views/RCTPicker.m +++ b/React/Views/RCTPicker.m @@ -9,37 +9,28 @@ #import "RCTPicker.h" -#import "RCTConvert.h" -#import "RCTEventDispatcher.h" #import "RCTUtils.h" #import "UIView+React.h" -const NSInteger UNINITIALIZED_INDEX = -1; - @interface RCTPicker() +@property (nonatomic, copy) NSArray *items; +@property (nonatomic, assign) NSInteger selectedIndex; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; + @end @implementation RCTPicker -{ - RCTEventDispatcher *_eventDispatcher; - NSArray *_items; - NSInteger _selectedIndex; -} -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - RCTAssertParam(eventDispatcher); - - if ((self = [super initWithFrame:CGRectZero])) { - _eventDispatcher = eventDispatcher; - _selectedIndex = UNINITIALIZED_INDEX; + if ((self = [super initWithFrame:frame])) { + _selectedIndex = NSNotFound; self.delegate = self; } return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)setItems:(NSArray *)items @@ -51,7 +42,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)setSelectedIndex:(NSInteger)selectedIndex { if (_selectedIndex != selectedIndex) { - BOOL animated = _selectedIndex != UNINITIALIZED_INDEX; // Don't animate the initial value + BOOL animated = _selectedIndex != NSNotFound; // Don't animate the initial value _selectedIndex = selectedIndex; dispatch_async(dispatch_get_main_queue(), ^{ [self selectRow:selectedIndex inComponent:0 animated:animated]; @@ -94,13 +85,12 @@ numberOfRowsInComponent:(__unused NSInteger)component didSelectRow:(NSInteger)row inComponent:(__unused NSInteger)component { _selectedIndex = row; - NSDictionary *event = @{ - @"target": self.reactTag, - @"newIndex": @(row), - @"newValue": [self valueForRow:row] - }; - - [_eventDispatcher sendInputEventWithName:@"change" body:event]; + if (_onChange) { + _onChange(@{ + @"newIndex": @(row), + @"newValue": [self valueForRow:row] + }); + } } @end diff --git a/React/Views/RCTPickerManager.m b/React/Views/RCTPickerManager.m index 6de76bcb7..8d1e18120 100644 --- a/React/Views/RCTPickerManager.m +++ b/React/Views/RCTPickerManager.m @@ -19,7 +19,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTPicker alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTPicker new]; } RCT_EXPORT_VIEW_PROPERTY(items, NSDictionaryArray) diff --git a/React/Views/RCTSegmentedControl.h b/React/Views/RCTSegmentedControl.h index 3e95735bd..500a236e9 100644 --- a/React/Views/RCTSegmentedControl.h +++ b/React/Views/RCTSegmentedControl.h @@ -9,13 +9,12 @@ #import -@class RCTEventDispatcher; +#import "RCTComponent.h" @interface RCTSegmentedControl : UISegmentedControl -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - @property (nonatomic, copy) NSArray *values; @property (nonatomic, assign) NSInteger selectedIndex; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; @end diff --git a/React/Views/RCTSegmentedControl.m b/React/Views/RCTSegmentedControl.m index 1b28bacc7..543de9c4f 100644 --- a/React/Views/RCTSegmentedControl.m +++ b/React/Views/RCTSegmentedControl.m @@ -14,17 +14,13 @@ #import "UIView+React.h" @implementation RCTSegmentedControl -{ - RCTEventDispatcher *_eventDispatcher; -} -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - if ((self = [super initWithFrame:CGRectZero])) { - _eventDispatcher = eventDispatcher; + if ((self = [super initWithFrame:frame])) { _selectedIndex = self.selectedSegmentIndex; - [self addTarget:self action:@selector(onChange:) - forControlEvents:UIControlEventValueChanged]; + [self addTarget:self action:@selector(didChange) + forControlEvents:UIControlEventValueChanged]; } return self; } @@ -45,14 +41,14 @@ super.selectedSegmentIndex = selectedIndex; } -- (void)onChange:(UISegmentedControl *)sender +- (void)didChange { - NSDictionary *event = @{ - @"target": self.reactTag, - @"value": [self titleForSegmentAtIndex:sender.selectedSegmentIndex], - @"selectedSegmentIndex": @(sender.selectedSegmentIndex) - }; - [_eventDispatcher sendInputEventWithName:@"change" body:event]; + if (_onChange) { + _onChange(@{ + @"value": [self titleForSegmentAtIndex:_selectedIndex], + @"selectedSegmentIndex": @(_selectedIndex) + }); + } } @end diff --git a/React/Views/RCTSegmentedControlManager.m b/React/Views/RCTSegmentedControlManager.m index 1c5621e7a..cbe9f4a4b 100644 --- a/React/Views/RCTSegmentedControlManager.m +++ b/React/Views/RCTSegmentedControlManager.m @@ -19,7 +19,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTSegmentedControl alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTSegmentedControl new]; } RCT_EXPORT_VIEW_PROPERTY(values, NSStringArray) @@ -27,6 +27,7 @@ RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger) RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(momentary, BOOL) RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL) +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock) - (NSDictionary *)constantsToExport { diff --git a/React/Views/RCTShadowView.h b/React/Views/RCTShadowView.h index 7211b17a7..9aa7f0299 100644 --- a/React/Views/RCTShadowView.h +++ b/React/Views/RCTShadowView.h @@ -39,7 +39,7 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry); @property (nonatomic, copy) NSString *viewName; @property (nonatomic, strong) UIColor *backgroundColor; // Used to propagate to children @property (nonatomic, assign) RCTUpdateLifecycle layoutLifecycle; -@property (nonatomic, assign) BOOL onLayout; +@property (nonatomic, copy) RCTDirectEventBlock onLayout; /** * isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is diff --git a/React/Views/RCTSlider.h b/React/Views/RCTSlider.h index 916419a29..664b0689b 100644 --- a/React/Views/RCTSlider.h +++ b/React/Views/RCTSlider.h @@ -9,6 +9,10 @@ #import +#import "RCTComponent.h" + @interface RCTSlider : UISlider +@property (nonatomic, copy) RCTBubblingEventBlock onChange; + @end diff --git a/React/Views/RCTSliderManager.m b/React/Views/RCTSliderManager.m index 9ce7699d1..694e8855b 100644 --- a/React/Views/RCTSliderManager.m +++ b/React/Views/RCTSliderManager.m @@ -27,25 +27,24 @@ RCT_EXPORT_MODULE() return slider; } -static void RCTSendSliderEvent(RCTSliderManager *self, UISlider *sender, BOOL continuous) +static void RCTSendSliderEvent(RCTSlider *sender, BOOL continuous) { - NSDictionary *event = @{ - @"target": sender.reactTag, - @"value": @(sender.value), - @"continuous": @(continuous), - }; - - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event]; + if (sender.onChange) { + sender.onChange(@{ + @"value": @(sender.value), + @"continuous": @(continuous), + }); + } } -- (void)sliderValueChanged:(UISlider *)sender +- (void)sliderValueChanged:(RCTSlider *)sender { - RCTSendSliderEvent(self, sender, YES); + RCTSendSliderEvent(sender, YES); } -- (void)sliderTouchEnd:(UISlider *)sender +- (void)sliderTouchEnd:(RCTSlider *)sender { - RCTSendSliderEvent(self, sender, NO); + RCTSendSliderEvent(sender, NO); } RCT_EXPORT_VIEW_PROPERTY(value, float); @@ -53,5 +52,6 @@ RCT_EXPORT_VIEW_PROPERTY(minimumValue, float); RCT_EXPORT_VIEW_PROPERTY(maximumValue, float); RCT_EXPORT_VIEW_PROPERTY(minimumTrackTintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(maximumTrackTintColor, UIColor); +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock); @end diff --git a/React/Views/RCTSwitch.h b/React/Views/RCTSwitch.h index 5c01abf49..f94bd5052 100644 --- a/React/Views/RCTSwitch.h +++ b/React/Views/RCTSwitch.h @@ -10,8 +10,11 @@ #import +#import "RCTComponent.h" + @interface RCTSwitch : UISwitch @property (nonatomic, assign) BOOL wasOn; +@property (nonatomic, copy) RCTBubblingEventBlock onChange; @end diff --git a/React/Views/RCTSwitchManager.m b/React/Views/RCTSwitchManager.m index 42a54210c..7fe065baa 100644 --- a/React/Views/RCTSwitchManager.m +++ b/React/Views/RCTSwitchManager.m @@ -30,11 +30,9 @@ RCT_EXPORT_MODULE() - (void)onChange:(RCTSwitch *)sender { if (sender.wasOn != sender.on) { - [self.bridge.eventDispatcher sendInputEventWithName:@"change" body:@{ - @"target": sender.reactTag, - @"value": @(sender.on) - }]; - + if (sender.onChange) { + sender.onChange(@{ @"value": @(sender.on) }); + } sender.wasOn = sender.on; } } @@ -43,6 +41,7 @@ RCT_EXPORT_VIEW_PROPERTY(onTintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor); RCT_EXPORT_VIEW_PROPERTY(thumbTintColor, UIColor); RCT_REMAP_VIEW_PROPERTY(value, on, BOOL); +RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock); RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RCTSwitch) { if (json) { diff --git a/React/Views/RCTTabBar.h b/React/Views/RCTTabBar.h index 5c24b9039..ba0bbd7e6 100644 --- a/React/Views/RCTTabBar.h +++ b/React/Views/RCTTabBar.h @@ -9,14 +9,10 @@ #import -@class RCTEventDispatcher; - @interface RCTTabBar : UIView @property (nonatomic, strong) UIColor *tintColor; @property (nonatomic, strong) UIColor *barTintColor; @property (nonatomic, assign) BOOL translucent; -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - @end diff --git a/React/Views/RCTTabBar.m b/React/Views/RCTTabBar.m index 75e017a98..13d60f113 100644 --- a/React/Views/RCTTabBar.m +++ b/React/Views/RCTTabBar.m @@ -25,17 +25,13 @@ @implementation RCTTabBar { BOOL _tabsChanged; - RCTEventDispatcher *_eventDispatcher; UITabBarController *_tabController; NSMutableArray *_tabViews; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - RCTAssertParam(eventDispatcher); - - if ((self = [super initWithFrame:CGRectZero])) { - _eventDispatcher = eventDispatcher; + if ((self = [super initWithFrame:frame])) { _tabViews = [NSMutableArray new]; _tabController = [UITabBarController new]; _tabController.delegate = self; @@ -44,7 +40,6 @@ return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (UIViewController *)reactViewController @@ -100,8 +95,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) for (RCTTabBarItem *tab in [self reactSubviews]) { UIViewController *controller = tab.reactViewController; if (!controller) { - controller = [[RCTWrapperViewController alloc] initWithContentView:tab - eventDispatcher:_eventDispatcher]; + controller = [[RCTWrapperViewController alloc] initWithContentView:tab]; } [viewControllers addObject:controller]; } @@ -154,7 +148,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) { NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController]; RCTTabBarItem *tab = [self reactSubviews][index]; - [_eventDispatcher sendInputEventWithName:@"press" body:@{@"target": tab.reactTag}]; + if (tab.onPress) tab.onPress(nil); return NO; } diff --git a/React/Views/RCTTabBarItem.h b/React/Views/RCTTabBarItem.h index 8fe6d8efb..def1abf6c 100644 --- a/React/Views/RCTTabBarItem.h +++ b/React/Views/RCTTabBarItem.h @@ -9,10 +9,13 @@ #import +#import "RCTComponent.h" + @interface RCTTabBarItem : UIView @property (nonatomic, copy) id icon; @property (nonatomic, assign, getter=isSelected) BOOL selected; @property (nonatomic, readonly) UITabBarItem *barItem; +@property (nonatomic, copy) RCTBubblingEventBlock onPress; @end diff --git a/React/Views/RCTTabBarItemManager.m b/React/Views/RCTTabBarItemManager.m index f91153efd..a926a54f3 100644 --- a/React/Views/RCTTabBarItemManager.m +++ b/React/Views/RCTTabBarItemManager.m @@ -21,10 +21,11 @@ RCT_EXPORT_MODULE() return [RCTTabBarItem new]; } -RCT_EXPORT_VIEW_PROPERTY(selected, BOOL); -RCT_EXPORT_VIEW_PROPERTY(icon, id); -RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, UIImage); -RCT_REMAP_VIEW_PROPERTY(badge, barItem.badgeValue, NSString); +RCT_EXPORT_VIEW_PROPERTY(selected, BOOL) +RCT_EXPORT_VIEW_PROPERTY(icon, id) +RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, UIImage) +RCT_REMAP_VIEW_PROPERTY(badge, barItem.badgeValue, NSString) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem) { view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title; diff --git a/React/Views/RCTTabBarManager.m b/React/Views/RCTTabBarManager.m index 7b9616246..b5d79f886 100644 --- a/React/Views/RCTTabBarManager.m +++ b/React/Views/RCTTabBarManager.m @@ -18,7 +18,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTTabBar alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTTabBar new]; } RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor) diff --git a/React/Views/RCTView.h b/React/Views/RCTView.h index 6e7019f58..1222dc9cf 100644 --- a/React/Views/RCTView.h +++ b/React/Views/RCTView.h @@ -11,17 +11,20 @@ #import +#import "RCTComponent.h" #import "RCTPointerEvents.h" @protocol RCTAutoInsetsProtocol; @class RCTView; -typedef void (^RCTViewEventHandler)(RCTView *view); @interface RCTView : UIView -@property (nonatomic, copy) RCTViewEventHandler accessibilityTapHandler; -@property (nonatomic, copy) RCTViewEventHandler magicTapHandler; +/** + * Accessibility event handlers + */ +@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityTap; +@property (nonatomic, copy) RCTDirectEventBlock onMagicTap; /** * Used to control how touch events are processed. diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m index d242faa44..85ff2ecf4 100644 --- a/React/Views/RCTView.m +++ b/React/Views/RCTView.m @@ -167,8 +167,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) - (BOOL)accessibilityActivate { - if (self.accessibilityTapHandler) { - self.accessibilityTapHandler(self); + if (_onAccessibilityTap) { + _onAccessibilityTap(nil); return YES; } else { return NO; @@ -177,8 +177,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) - (BOOL)accessibilityPerformMagicTap { - if (self.magicTapHandler) { - self.magicTapHandler(self); + if (_onMagicTap) { + _onMagicTap(nil); return YES; } else { return NO; diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index f3c370b94..35c2a4d4f 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -11,10 +11,12 @@ #import "RCTBridgeModule.h" #import "RCTConvert.h" +#import "RCTComponent.h" +#import "RCTDefines.h" +#import "RCTEventDispatcher.h" #import "RCTLog.h" @class RCTBridge; -@class RCTEventDispatcher; @class RCTShadowView; @class RCTSparseArray; @class RCTUIManager; @@ -37,7 +39,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v * return a fresh instance each time. The view module MUST NOT cache the returned * view and return the same instance for subsequent calls. */ -- (UIView *)view; +- (UIView *)view; /** * This method instantiates a native view using the props passed into the component. @@ -57,6 +59,8 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v - (RCTShadowView *)shadowView; /** + * DEPRECATED: declare properties of type RCTBubblingEventBlock instead + * * Returns an array of names of events that can be sent by native views. This * should return bubbling, directly-dispatched event types. The event name * should not include a prefix such as 'on' or 'top', as this will be applied @@ -69,6 +73,8 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v - (NSArray *)customBubblingEventTypes; /** + * DEPRECATED: declare properties of type RCTDirectEventBlock instead + * * Returns an array of names of events that can be sent by native views. This * should return non-bubbling, directly-dispatched event types. The event name * should not include a prefix such as 'on' or 'top', as this will be applied diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index b4562f689..c2cf93676 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -54,7 +54,7 @@ RCT_EXPORT_MODULE() return _bridge.uiManager.methodQueue; } -- (UIView *)viewWithProps:(NSDictionary *)props +- (UIView *)viewWithProps:(__unused NSDictionary *)props { return [self view]; } @@ -76,7 +76,6 @@ RCT_EXPORT_MODULE() // Generic events @"press", @"change", - @"change", @"focus", @"blur", @"submitEditing", @@ -92,11 +91,7 @@ RCT_EXPORT_MODULE() - (NSArray *)customDirectEventTypes { - return @[ - @"layout", - @"accessibilityTap", - @"magicTap", - ]; + return @[]; } - (NSDictionary *)constantsToExport @@ -195,27 +190,8 @@ RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView) view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth; } } -RCT_CUSTOM_VIEW_PROPERTY(onAccessibilityTap, BOOL, __unused RCTView) -{ - view.accessibilityTapHandler = [self eventHandlerWithName:@"accessibilityTap" json:json]; -} -RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, __unused RCTView) -{ - view.magicTapHandler = [self eventHandlerWithName:@"magicTap" json:json]; -} - -- (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json -{ - RCTViewEventHandler handler = nil; - if ([RCTConvert BOOL:json]) { - __weak RCTViewManager *weakSelf = self; - handler = ^(RCTView *tappedView) { - NSDictionary *body = @{ @"target": tappedView.reactTag }; - [weakSelf.bridge.eventDispatcher sendInputEventWithName:eventName body:body]; - }; - } - return handler; -} +RCT_EXPORT_VIEW_PROPERTY(onAccessibilityTap, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onMagicTap, RCTDirectEventBlock) #define RCT_VIEW_BORDER_PROPERTY(SIDE) \ RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, CGFloat, RCTView) \ @@ -291,6 +267,6 @@ RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t) RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t) RCT_EXPORT_SHADOW_PROPERTY(position, css_position_type_t) -RCT_EXPORT_SHADOW_PROPERTY(onLayout, BOOL) +RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock) @end diff --git a/React/Views/RCTWebView.h b/React/Views/RCTWebView.h index 9a028ea49..fdb192a39 100644 --- a/React/Views/RCTWebView.h +++ b/React/Views/RCTWebView.h @@ -17,8 +17,6 @@ */ extern NSString *const RCTJSNavigationScheme; -@class RCTEventDispatcher; - @interface RCTWebView : RCTView @property (nonatomic, strong) NSURL *URL; @@ -26,8 +24,6 @@ extern NSString *const RCTJSNavigationScheme; @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, copy) NSString *injectedJavaScript; -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - - (void)goForward; - (void)goBack; - (void)reload; diff --git a/React/Views/RCTWebView.m b/React/Views/RCTWebView.m index 196284d24..4838179a5 100644 --- a/React/Views/RCTWebView.m +++ b/React/Views/RCTWebView.m @@ -22,24 +22,24 @@ NSString *const RCTJSNavigationScheme = @"react-js-navigation"; @interface RCTWebView () +@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; +@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; +@property (nonatomic, copy) RCTDirectEventBlock onLoadingError; + @end @implementation RCTWebView { - RCTEventDispatcher *_eventDispatcher; UIWebView *_webView; NSString *_injectedJavaScript; } -- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher +- (instancetype)initWithFrame:(CGRect)frame { - RCTAssertParam(eventDispatcher); - - if ((self = [super initWithFrame:CGRectZero])) { + if ((self = [super initWithFrame:frame])) { super.backgroundColor = [UIColor clearColor]; _automaticallyAdjustContentInsets = YES; _contentInset = UIEdgeInsetsZero; - _eventDispatcher = eventDispatcher; _webView = [[UIWebView alloc] initWithFrame:self.bounds]; _webView.delegate = self; [self addSubview:_webView]; @@ -47,7 +47,6 @@ NSString *const RCTJSNavigationScheme = @"react-js-navigation"; return self; } -RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame) RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)goForward @@ -123,13 +122,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (NSMutableDictionary *)baseEvent { - NSURL *url = _webView.request.URL; - NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary: @{ - @"target": self.reactTag, - @"url": url ? url.absoluteString : @"", + @"url": _webView.request.URL.absoluteString ?: @"", @"loading" : @(_webView.loading), - @"title": title, + @"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"], @"canGoBack": @(_webView.canGoBack), @"canGoForward" : @(_webView.canGoForward), }]; @@ -139,19 +135,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) #pragma mark - UIWebViewDelegate methods - - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { - // We have this check to filter out iframe requests and whatnot - BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; - if (isTopFrame) { - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary: @{ - @"url": (request.URL).absoluteString, - @"navigationType": @(navigationType) - }]; - [_eventDispatcher sendInputEventWithName:@"loadingStart" body:event]; + if (_onLoadingStart) { + // We have this check to filter out iframe requests and whatnot + BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; + if (isTopFrame) { + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"url": (request.URL).absoluteString, + @"navigationType": @(navigationType) + }]; + _onLoadingStart(event); + } } // JS Navigation handler @@ -160,21 +157,24 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error { - if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { - // NSURLErrorCancelled is reported when a page has a redirect OR if you load - // a new URL in the WebView before the previous one came back. We can just - // ignore these since they aren't real errors. - // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os - return; - } + if (_onLoadingError) { - NSMutableDictionary *event = [self baseEvent]; - [event addEntriesFromDictionary: @{ - @"domain": error.domain, - @"code": @(error.code), - @"description": error.localizedDescription, - }]; - [_eventDispatcher sendInputEventWithName:@"loadingError" body:event]; + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { + // NSURLErrorCancelled is reported when a page has a redirect OR if you load + // a new URL in the WebView before the previous one came back. We can just + // ignore these since they aren't real errors. + // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os + return; + } + + NSMutableDictionary *event = [self baseEvent]; + [event addEntriesFromDictionary: @{ + @"domain": error.domain, + @"code": @(error.code), + @"description": error.localizedDescription, + }]; + _onLoadingError(event); + } } - (void)webViewDidFinishLoad:(UIWebView *)webView @@ -184,8 +184,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) } // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. - if (!webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { - [_eventDispatcher sendInputEventWithName:@"loadingFinish" body:[self baseEvent]]; + if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { + _onLoadingFinish([self baseEvent]); } } diff --git a/React/Views/RCTWebViewManager.m b/React/Views/RCTWebViewManager.m index 1fd2cfe3f..8779a970b 100644 --- a/React/Views/RCTWebViewManager.m +++ b/React/Views/RCTWebViewManager.m @@ -20,7 +20,7 @@ RCT_EXPORT_MODULE() - (UIView *)view { - return [[RCTWebView alloc] initWithEventDispatcher:self.bridge.eventDispatcher]; + return [RCTWebView new]; } RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL); @@ -31,15 +31,9 @@ RCT_REMAP_VIEW_PROPERTY(scalesPageToFit, _webView.scalesPageToFit, BOOL); RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString); RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets); RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL); - -- (NSArray *)customDirectEventTypes -{ - return @[ - @"loadingStart", - @"loadingFinish", - @"loadingError", - ]; -} +RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock); +RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock); - (NSDictionary *)constantsToExport { @@ -80,7 +74,6 @@ RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) }]; } - RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { diff --git a/React/Views/RCTWrapperViewController.h b/React/Views/RCTWrapperViewController.h index 09e647891..7b24ae7d5 100644 --- a/React/Views/RCTWrapperViewController.h +++ b/React/Views/RCTWrapperViewController.h @@ -11,7 +11,6 @@ #import "RCTViewControllerProtocol.h" -@class RCTEventDispatcher; @class RCTNavItem; @class RCTWrapperViewController; @@ -24,11 +23,8 @@ didMoveToNavigationController:(UINavigationController *)navigationController; @interface RCTWrapperViewController : UIViewController -- (instancetype)initWithContentView:(UIView *)contentView - eventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER; - -- (instancetype)initWithNavItem:(RCTNavItem *)navItem - eventDispatcher:(RCTEventDispatcher *)eventDispatcher; +- (instancetype)initWithContentView:(UIView *)contentView NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithNavItem:(RCTNavItem *)navItem; @property (nonatomic, weak) id navigationListener; @property (nonatomic, strong) RCTNavItem *navItem; diff --git a/React/Views/RCTWrapperViewController.m b/React/Views/RCTWrapperViewController.m index 00af3b53d..92707e683 100644 --- a/React/Views/RCTWrapperViewController.m +++ b/React/Views/RCTWrapperViewController.m @@ -21,7 +21,6 @@ { UIView *_wrapperView; UIView *_contentView; - RCTEventDispatcher *_eventDispatcher; CGFloat _previousTopLayout; CGFloat _previousBottomLayout; } @@ -30,23 +29,19 @@ @synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide; - (instancetype)initWithContentView:(UIView *)contentView - eventDispatcher:(RCTEventDispatcher *)eventDispatcher { RCTAssertParam(contentView); - RCTAssertParam(eventDispatcher); if ((self = [super initWithNibName:nil bundle:nil])) { _contentView = contentView; - _eventDispatcher = eventDispatcher; self.automaticallyAdjustsScrollViewInsets = NO; } return self; } - (instancetype)initWithNavItem:(RCTNavItem *)navItem - eventDispatcher:(RCTEventDispatcher *)eventDispatcher { - if ((self = [self initWithContentView:navItem eventDispatcher:eventDispatcher])) { + if ((self = [self initWithContentView:navItem])) { _navItem = navItem; } return self; @@ -101,14 +96,8 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view) UINavigationItem *item = self.navigationItem; item.title = _navItem.title; item.backBarButtonItem = _navItem.backButtonItem; - if ((item.leftBarButtonItem = _navItem.leftButtonItem)) { - item.leftBarButtonItem.target = self; - item.leftBarButtonItem.action = @selector(handleNavLeftButtonTapped); - } - if ((item.rightBarButtonItem = _navItem.rightButtonItem)) { - item.rightBarButtonItem.target = self; - item.rightBarButtonItem.action = @selector(handleNavRightButtonTapped); - } + item.leftBarButtonItem = _navItem.leftButtonItem; + item.rightBarButtonItem = _navItem.rightButtonItem; } } @@ -122,18 +111,6 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view) self.view = _wrapperView; } -- (void)handleNavLeftButtonTapped -{ - [_eventDispatcher sendInputEventWithName:@"navLeftButtonTap" - body:@{@"target":_navItem.reactTag}]; -} - -- (void)handleNavRightButtonTapped -{ - [_eventDispatcher sendInputEventWithName:@"navRightButtonTap" - body:@{@"target":_navItem.reactTag}]; -} - - (void)didMoveToParentViewController:(UIViewController *)parent { // There's no clear setter for navigation controllers, but did move to parent diff --git a/React/Views/UIView+React.m b/React/Views/UIView+React.m index 18bd5a747..1cbbc16da 100644 --- a/React/Views/UIView+React.m +++ b/React/Views/UIView+React.m @@ -23,7 +23,7 @@ - (void)setReactTag:(NSNumber *)reactTag { - objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_COPY_NONATOMIC); + objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (BOOL)isReactRootView From eb9a9395ac05f71f7f2aaebdef424d5ee1a8a2e8 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Wed, 2 Sep 2015 08:54:59 -0700 Subject: [PATCH 18/27] [ReactNative][JSCProfiler] Cleanup make file and make targets cacheable Summary: The makefile for the JSCLegacyProfiler was really messy, clean it up a bit, remove duplicate code, and make targets be actual files rather than dummy targets so it can actually cache things instead of re-downloading everything every time. --- JSCLegacyProfiler/JSCLegacyProfiler.mm | 2 - JSCLegacyProfiler/Makefile | 179 ++++++++++++------------- 2 files changed, 84 insertions(+), 97 deletions(-) diff --git a/JSCLegacyProfiler/JSCLegacyProfiler.mm b/JSCLegacyProfiler/JSCLegacyProfiler.mm index dd2f01f8a..e906eda0f 100644 --- a/JSCLegacyProfiler/JSCLegacyProfiler.mm +++ b/JSCLegacyProfiler/JSCLegacyProfiler.mm @@ -1,5 +1,3 @@ -//#include "config.h" - #include "JSCLegacyProfiler.h" #include "APICast.h" diff --git a/JSCLegacyProfiler/Makefile b/JSCLegacyProfiler/Makefile index a1f704d98..306c6632c 100644 --- a/JSCLegacyProfiler/Makefile +++ b/JSCLegacyProfiler/Makefile @@ -1,112 +1,101 @@ -HEADER_PATHS := `find ./tmp/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"` -SDK_VERSION=$(shell plutil -convert json -o - /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/SDKSettings.plist | awk -f parseSDKVersion.awk) +HEADER_PATHS := `find download/JavaScriptCore -name '*.h' | xargs -I{} dirname {} | uniq | xargs -I{} echo "-I {}"` + +SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/$1.platform/Developer/SDKs/$1.sdk + +SDK_VERSION = $(shell plutil -convert json -o - $(call SDK_PATH,iPhoneOS)/SDKSettings.plist | awk -f parseSDKVersion.awk) + CERT ?= "iPhone Developer" +ARCHS = x86_64 arm64 armv7 i386 + +PLATFORM = \ + if [[ "$*" = "x86_64" || "$*" = "i386" ]]; then \ + PLATFORM=iPhoneSimulator; \ + else \ + PLATFORM=iPhoneOS; \ + fi; + +SYSROOT = -isysroot $(call SDK_PATH,$${PLATFORM}) + +IOS8_LIBS = download/WebCore/WebCore-7600.1.25 download/WTF/WTF-7600.1.24 download/JavaScriptCore/JavaScriptCore-7600.1.17 download/JavaScriptCore/JavaScriptCore-7600.1.17/Bytecodes.h libyajl.a + +ios8: RCTJSCProfiler.ios8.dylib /tmp/RCTJSCProfiler ifneq ($(SDK_VERSION), 8) -all: $(error "Expected to be compiled with iOS SDK version 8, found $(SDK_VERSION)") +else + cp $^ endif -ios8: prepare build generate +/tmp/JSCProfiler: + mkdir -p $@ -prepare: clean create download +.PRECIOUS: RCTJSCProfiler.ios8.dylib +RCTJSCProfiler.ios8.dylib: RCTJSCProfiler_unsigned.ios8.dylib + cp $< $@ + codesign -f -s ${CERT} $@ || rm $@ -build: x86_64 arm64 armv7 +.PRECIOUS: RCTJSCProfiler_unsigned.ios8.dylib +RCTJSCProfiler_unsigned.ios8.dylib: $(patsubst %,RCTJSCProfiler_%.ios8.dylib,$(ARCHS)) + lipo -create -output $@ $^ -generate: lipo codesign +.PRECIOUS: RCTJSCProfiler_%.ios8.dylib +RCTJSCProfiler_%.ios8.dylib: $(IOS8_LIBS) + $(PLATFORM) \ + clang -w -dynamiclib -o RCTJSCProfiler_$*.ios8.dylib -std=c++11 \ + -arch $* \ + -install_name RCTJSCProfiler.ios8.dylib \ + -include ./download/JavaScriptCore/JavaScriptCore-7600.1.17/config.h \ + -I download \ + -I download/WebCore/WebCore-7600.1.25/icu \ + -I download/WTF/WTF-7600.1.24 \ + -I download/yajl-2.1.0/build/yajl-2.1.0/include \ + -DNDEBUG=1\ + -miphoneos-version-min=8.0 \ + $(SYSROOT) \ + $(HEADER_PATHS) \ + -undefined dynamic_lookup \ + JSCLegacyProfiler.mm libyajl.a -clean: - @rm -rf tmp/ /tmp/RCTJSCProfiler +.PRECIOUS: %/Bytecodes.h +%/Bytecodes.h: + python $*/generate-bytecode-files --bytecodes_h $@ $*/bytecode/BytecodeList.json -lipo: - lipo -create -output /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib ./tmp/RCTJSCProfiler_x86_64 ./tmp/RCTJSCProfiler_arm64 ./tmp/RCTJSCProfiler_armv7 +.PRECIOUS: libyajl.a +libyajl.a: $(patsubst %,libyajl_%.a,$(ARCHS)) + lipo -create $^ -output $@ -codesign: - codesign -f -s ${CERT} /tmp/RCTJSCProfiler/RCTJSCProfiler.ios8.dylib - -create: - mkdir -p ./tmp /tmp/RCTJSCProfiler/ ./tmp/CoreFoundation ./tmp/Foundation - for file in ./tmp/CoreFoundation/CFUserNotification.h ./tmp/CoreFoundation/CFXMLNode.h ./tmp/CoreFoundation/CFXMLParser.h ./tmp/Foundation/Foundation.h; do echo '' > "$$file"; done - -download: wtf jsc webcore yajl - -wtf: - curl -o tmp/WTF.tar.gz http://www.opensource.apple.com/tarballs/WTF/WTF-7600.1.24.tar.gz - tar -zxvf tmp/WTF.tar.gz -C tmp - -jsc: - curl -o tmp/JSC.tar.gz http://www.opensource.apple.com/tarballs/JavaScriptCore/JavaScriptCore-7600.1.17.tar.gz - tar -zxvf tmp/JSC.tar.gz -C tmp - mv ./tmp/JavaScriptCore-7600.1.17 ./tmp/JavaScriptCore - python ./tmp/JavaScriptCore/generate-bytecode-files --bytecodes_h ./tmp/JavaScriptCore/Bytecodes.h ./tmp/JavaScriptCore/bytecode/BytecodeList.json - -webcore: - curl -o tmp/WebCore.tar.gz http://www.opensource.apple.com/tarballs/WebCore/WebCore-7600.1.25.tar.gz - tar -zxvf tmp/WebCore.tar.gz -C tmp - -yajl: - curl -o tmp/yajl.tar.gz https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0 - tar -zxvf tmp/yajl.tar.gz -C tmp - mkdir -p ./tmp/yajl-2.1.0/build && cd ./tmp/yajl-2.1.0/build && cmake .. && make - echo `find . -name '*.c'` - cd ./tmp/yajl-2.1.0/src && \ - clang -arch arm64 -arch armv7 -std=c99 \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/ \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/machine \ +.PRECIOUS: libyajl_%.a +libyajl_%.a: download/yajl-2.1.0 + $(PLATFORM) \ + cd download/yajl-2.1.0/src; \ + clang -arch $(*F) -std=c99 \ + -miphoneos-version-min=8.0 \ + $(SYSROOT) \ -I ../build/yajl-2.1.0/include \ -c `find . -name '*.c'` - libtool -static -o ./tmp/yajl.a `find ./tmp/yajl-2.1.0/src/ -name '*.o'` + find download/yajl-2.1.0/src/ -name '*.o' -exec libtool -static -o $@ {} + -x86_64: - clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_x86_64 -std=c++11 \ - -install_name RCTJSCProfiler.ios8.dylib \ - -include ./tmp/JavaScriptCore/config.h \ - -I ./tmp \ - -I ./tmp/WebCore-7600.1.25/icu \ - -I ./tmp/WTF-7600.1.24 \ - -I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \ - -DNDEBUG=1\ - -miphoneos-version-min=8.0 \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/system \ - ${HEADER_PATHS} \ - -undefined dynamic_lookup \ - ./JSCLegacyProfiler.mm ./tmp/yajl-2.1.0/build/yajl-2.1.0/lib/libyajl_s.a +.PRECIOUS: download/yajl-2.1.0 +download/yajl-2.1.0: download/yajl-2.1.0.tar.gz + tar -zxvf $< -C download + mkdir -p download/yajl-2.1.0/build && cd download/yajl-2.1.0/build && cmake .. -arm64: - echo $(HEADER_PATHS) - clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_arm64 -std=c++11 \ - -install_name RCTJSCProfiler.ios8.dylib \ - -arch arm64 \ - -include ./tmp/JavaScriptCore/config.h \ - -I ./tmp \ - -I ./tmp/WebCore-7600.1.25/icu \ - -I ./tmp/WTF-7600.1.24 \ - -I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include/machine \ - -DNDEBUG=1\ - -miphoneos-version-min=8.0 \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/system \ - ${HEADER_PATHS} \ - -undefined dynamic_lookup \ - ./JSCLegacyProfiler.mm ./tmp/yajl.a +.PRECIOUS: download/yajl-2.1.0.tar.gz +download/yajl-2.1.0.tar.gz: + mkdir -p `dirname $@` + curl -o $@ https://codeload.github.com/lloyd/yajl/tar.gz/2.1.0 -armv7: - clang -w -dynamiclib -o ./tmp/RCTJSCProfiler_armv7 -std=c++11 \ - -install_name RCTJSCProfiler.ios8.dylib \ - -arch armv7 \ - -include ./tmp/JavaScriptCore/config.h \ - -I ./tmp \ - -I ./tmp/WebCore-7600.1.25/icu \ - -I ./tmp/WTF-7600.1.24 \ - -I ./tmp/yajl-2.1.0/build/yajl-2.1.0/include \ - -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include \ - -DNDEBUG=1\ - -miphoneos-version-min=8.0 \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib \ - -L /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/system \ - ${HEADER_PATHS} \ - -undefined dynamic_lookup \ - ./JSCLegacyProfiler.mm ./tmp/yajl.a +.PRECIOUS: download/% +download/%: download/%.tar.gz + tar -zxvf $< -C `dirname $@` + +.PRECIOUS: %.tar.gz +%.tar.gz: + mkdir -p `dirname $@` + curl -o $@ http://www.opensource.apple.com/tarballs/$(patsubst download/%,%,$@) + +.PHONY: clean +clean: + @rm -rf $(wildcard *.dylib) + @rm -rf $(wildcard *.a) + @rm -rf download From 36444a65c700f4658d54a6be32bae700b228f6da Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Wed, 2 Sep 2015 08:25:10 -0700 Subject: [PATCH 19/27] Add pluggable image processing system --- .../UIExplorer.xcodeproj/project.pbxproj | 10 + .../RCTImageLoaderHelpers.h | 47 +++ .../RCTImageLoaderHelpers.m | 105 ++++++ .../UIExplorerUnitTests/RCTImageLoaderTests.m | 148 +++++++++ ...tHandler.h => RCTAssetBundleImageLoader.h} | 4 +- Libraries/Image/RCTAssetBundleImageLoader.m | 80 +++++ Libraries/Image/RCTAssetsLibraryImageLoader.h | 28 ++ Libraries/Image/RCTAssetsLibraryImageLoader.m | 155 +++++++++ Libraries/Image/RCTCameraRollManager.m | 1 + .../{RCTGIFImage.h => RCTGIFImageDecoder.h} | 9 +- .../{RCTGIFImage.m => RCTGIFImageDecoder.m} | 59 ++-- .../Image/RCTImage.xcodeproj/project.pbxproj | 36 +- Libraries/Image/RCTImageDownloader.h | 17 +- Libraries/Image/RCTImageDownloader.m | 81 ++--- Libraries/Image/RCTImageLoader.h | 85 ++++- Libraries/Image/RCTImageLoader.m | 307 +++++++----------- Libraries/Image/RCTImageRequestHandler.m | 70 ---- Libraries/Image/RCTImageStoreManager.h | 3 +- Libraries/Image/RCTImageStoreManager.m | 25 +- Libraries/Image/RCTImageView.m | 14 +- Libraries/Image/RCTPhotoLibraryImageLoader.h | 14 + Libraries/Image/RCTPhotoLibraryImageLoader.m | 82 +++++ 22 files changed, 985 insertions(+), 395 deletions(-) create mode 100644 Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h create mode 100644 Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m create mode 100644 Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m rename Libraries/Image/{RCTImageRequestHandler.h => RCTAssetBundleImageLoader.h} (76%) create mode 100644 Libraries/Image/RCTAssetBundleImageLoader.m create mode 100644 Libraries/Image/RCTAssetsLibraryImageLoader.h create mode 100644 Libraries/Image/RCTAssetsLibraryImageLoader.m rename Libraries/Image/{RCTGIFImage.h => RCTGIFImageDecoder.h} (56%) rename Libraries/Image/{RCTGIFImage.m => RCTGIFImageDecoder.m} (74%) delete mode 100644 Libraries/Image/RCTImageRequestHandler.m create mode 100644 Libraries/Image/RCTPhotoLibraryImageLoader.h create mode 100644 Libraries/Image/RCTPhotoLibraryImageLoader.m diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index fb20657a3..9ec430af5 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; 83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; }; + 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */; }; + 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */; }; 83A936C81B7E0F08005B9C36 /* RCTConvert_UIColorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -217,6 +219,9 @@ 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = ""; }; + 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderTests.m; sourceTree = ""; }; + 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoaderHelpers.m; sourceTree = ""; }; + 8385CF051B8747A000C6273E /* RCTImageLoaderHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTImageLoaderHelpers.h; sourceTree = ""; }; 83A936C71B7E0F08005B9C36 /* RCTConvert_UIColorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert_UIColorTests.m; sourceTree = ""; }; D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ @@ -365,6 +370,9 @@ 1497CFA81B21F5E400C1F8F2 /* RCTConvert_UIFontTests.m */, 1497CFA91B21F5E400C1F8F2 /* RCTEventDispatcherTests.m */, 1300627E1B59179B0043FE5A /* RCTGzipTests.m */, + 8385CF051B8747A000C6273E /* RCTImageLoaderHelpers.h */, + 8385CF031B87479200C6273E /* RCTImageLoaderHelpers.m */, + 8385CEF41B873B5C00C6273E /* RCTImageLoaderTests.m */, 144D21231B2204C5006DB32B /* RCTImageUtilTests.m */, 13DB03471B5D2ED500C27245 /* RCTJSONTests.m */, 13DF61B51B67A45000EDB188 /* RCTMethodArgumentTests.m */, @@ -811,6 +819,8 @@ 83A936C81B7E0F08005B9C36 /* RCTConvert_UIColorTests.m in Sources */, 13DF61B61B67A45000EDB188 /* RCTMethodArgumentTests.m in Sources */, 138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */, + 8385CF041B87479200C6273E /* RCTImageLoaderHelpers.m in Sources */, + 8385CEF51B873B5C00C6273E /* RCTImageLoaderTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h new file mode 100644 index 000000000..364cddc88 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.h @@ -0,0 +1,47 @@ +/** + * 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 "RCTImageLoader.h" + +typedef BOOL (^RCTImageURLLoaderCanLoadImageURLHandler)(NSURL *requestURL); +typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(NSURL *imageURL, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler); + +@interface RCTConcreteImageURLLoader : NSObject + +- (instancetype)initWithPriority:(float)priority + canLoadImageURLHandler:(RCTImageURLLoaderCanLoadImageURLHandler)canLoadImageURLHandler + loadImageURLHandler:(RCTImageURLLoaderLoadImageURLHandler)loadImageURLHandler; + +@end + +typedef BOOL (^RCTImageDecoderCanDecodeImageDataHandler)(NSData *imageData); +typedef RCTImageLoaderCancellationBlock (^RCTImageDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler); + +@interface RCTConcreteImageDecoder : NSObject + +- (instancetype)initWithPriority:(float)priority + canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler + decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler; + +@end + +#define _RCTDefineImageHandler(SUPERCLASS, CLASS_NAME) \ +@interface CLASS_NAME : SUPERCLASS @end \ +@implementation CLASS_NAME RCT_EXPORT_MODULE() @end + +#define RCTDefineImageURLLoader(CLASS_NAME) \ +_RCTDefineImageHandler(RCTConcreteImageURLLoader, CLASS_NAME) + +#define RCTDefineImageDecoder(CLASS_NAME) \ +_RCTDefineImageHandler(RCTConcreteImageDecoder, CLASS_NAME) diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m new file mode 100644 index 000000000..2038fad50 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderHelpers.m @@ -0,0 +1,105 @@ +/** + * 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 "RCTImageLoaderHelpers.h" + +@implementation RCTConcreteImageURLLoader +{ + RCTImageURLLoaderCanLoadImageURLHandler _canLoadImageURLHandler; + RCTImageURLLoaderLoadImageURLHandler _loadImageURLHandler; + float _priority; +} + ++ (NSString *)moduleName +{ + return nil; +} + +- (instancetype)init +{ + return nil; +} + +- (instancetype)initWithPriority:(float)priority canLoadImageURLHandler:(RCTImageURLLoaderCanLoadImageURLHandler)canLoadImageURLHandler loadImageURLHandler:(RCTImageURLLoaderLoadImageURLHandler)loadImageURLHandler +{ + if ((self = [super init])) { + _canLoadImageURLHandler = [canLoadImageURLHandler copy]; + _loadImageURLHandler = [loadImageURLHandler copy]; + _priority = priority; + } + + return self; +} + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return _canLoadImageURLHandler(requestURL); +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler); +} + +- (float)imageLoaderPriority +{ + return _priority; +} + +@end + +@implementation RCTConcreteImageDecoder +{ + RCTImageDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler; + RCTImageDecoderDecodeImageDataHandler _decodeImageDataHandler; + float _priority; +} + ++ (NSString *)moduleName +{ + return nil; +} + +- (instancetype)init +{ + return nil; +} + +- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler +{ + if ((self = [super init])) { + _canDecodeImageDataHandler = [canDecodeImageDataHandler copy]; + _decodeImageDataHandler = [decodeImageDataHandler copy]; + _priority = priority; + } + + return self; +} + +- (BOOL)canDecodeImageData:(NSData *)imageData +{ + return _canDecodeImageDataHandler(imageData); +} + +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler); +} + +- (float)imageDecoderPriority +{ + return _priority; +} + +@end diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m new file mode 100644 index 000000000..256f0e5c5 --- /dev/null +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m @@ -0,0 +1,148 @@ +/** + * 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 + +#import "RCTBridge.h" +#import "RCTImageLoader.h" +#import "RCTImageLoaderHelpers.h" + +unsigned char blackGIF[] = { + 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b +}; + +RCTDefineImageURLLoader(RCTImageLoaderTestsURLLoader1) +RCTDefineImageURLLoader(RCTImageLoaderTestsURLLoader2) +RCTDefineImageDecoder(RCTImageLoaderTestsDecoder1) +RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) + +@interface RCTImageLoaderTests : XCTestCase + +@end + +@implementation RCTImageLoaderTests + +- (void)testImageLoading +{ + UIImage *image = [UIImage new]; + + id loader = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { + return YES; + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { + progressHandler(1, 1); + completionHandler(nil, image); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader, imageLoader]; } launchOptions:nil]; + + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + XCTAssertEqual(progress, 1); + XCTAssertEqual(total, 1); + } completionBlock:^(NSError *loadError, id loadedImage) { + XCTAssertEqualObjects(loadedImage, image); + XCTAssertNil(loadError); + }]; + XCTAssertNil(cancelBlock); +} + +- (void)testImageLoaderUsesImageURLLoaderWithHighestPriority +{ + UIImage *image = [UIImage new]; + + id loader1 = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { + return YES; + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderProgressBlock progressHandler, RCTImageLoaderCompletionBlock completionHandler) { + progressHandler(1, 1); + completionHandler(nil, image); + return nil; + }]; + + id loader2 = [[RCTImageLoaderTestsURLLoader2 alloc] initWithPriority:0.5 canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) { + return YES; + } loadImageURLHandler:^RCTImageLoaderCancellationBlock(__unused NSURL *imageURL, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderProgressBlock progressHandler, __unused RCTImageLoaderCompletionBlock completionHandler) { + XCTFail(@"Should not have used loader2"); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2, imageLoader]; } launchOptions:nil]; + + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + XCTAssertEqual(progress, 1); + XCTAssertEqual(total, 1); + } completionBlock:^(NSError *loadError, id loadedImage) { + XCTAssertEqualObjects(loadedImage, image); + XCTAssertNil(loadError); + }]; + XCTAssertNil(cancelBlock); +} + +- (void)testImageDecoding +{ + NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO]; + UIImage *image = [[UIImage alloc] initWithData:data]; + + id decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + return YES; + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { + XCTAssertEqualObjects(imageData, data); + completionHandler(nil, image); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder, imageLoader]; } launchOptions:nil]; + + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) { + XCTAssertEqualObjects(decodedImage, image); + XCTAssertNil(decodeError); + }]; + XCTAssertNil(cancelBlock); +} + +- (void)testImageLoaderUsesImageDecoderWithHighestPriority +{ + NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO]; + UIImage *image = [[UIImage alloc] initWithData:data]; + + id decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + return YES; + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) { + XCTAssertEqualObjects(imageData, data); + completionHandler(nil, image); + return nil; + }]; + + id decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) { + return YES; + } decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) { + XCTFail(@"Should not have used decoder2"); + return nil; + }]; + + RCTImageLoader *imageLoader = [RCTImageLoader new]; + NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[decoder1, decoder2, imageLoader]; } launchOptions:nil]; + + RCTImageLoaderCancellationBlock cancelBlock = [imageLoader decodeImageData:data size:CGSizeMake(1, 1) scale:1.0 resizeMode:UIViewContentModeScaleToFill completionBlock:^(NSError *decodeError, id decodedImage) { + XCTAssertEqualObjects(decodedImage, image); + XCTAssertNil(decodeError); + }]; + XCTAssertNil(cancelBlock); +} + +@end diff --git a/Libraries/Image/RCTImageRequestHandler.h b/Libraries/Image/RCTAssetBundleImageLoader.h similarity index 76% rename from Libraries/Image/RCTImageRequestHandler.h rename to Libraries/Image/RCTAssetBundleImageLoader.h index 0f0359885..ff460e08e 100644 --- a/Libraries/Image/RCTImageRequestHandler.h +++ b/Libraries/Image/RCTAssetBundleImageLoader.h @@ -7,8 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTURLRequestHandler.h" +#import "RCTImageLoader.h" -@interface RCTImageRequestHandler : NSObject +@interface RCTAssetBundleImageLoader : NSObject @end diff --git a/Libraries/Image/RCTAssetBundleImageLoader.m b/Libraries/Image/RCTAssetBundleImageLoader.m new file mode 100644 index 000000000..fb9aaa057 --- /dev/null +++ b/Libraries/Image/RCTAssetBundleImageLoader.m @@ -0,0 +1,80 @@ +/** + * 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 "RCTAssetBundleImageLoader.h" + +#import "RCTUtils.h" + +@implementation RCTAssetBundleImageLoader + +RCT_EXPORT_MODULE() + +- (NSString *)imageNameForRequestURL:(NSURL *)requestURL +{ + if (!requestURL.fileURL) { + return nil; + } + + NSString *resourcesPath = [NSBundle mainBundle].resourcePath; + NSString *requestPath = requestURL.absoluteURL.path; + if (requestPath.length < resourcesPath.length + 1) { + return nil; + } + + return [requestPath substringFromIndex:resourcesPath.length + 1]; +} + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + NSString *imageName = [self imageNameForRequestURL:requestURL]; + if (!imageName.length) { + return NO; + } + + if ([[NSBundle mainBundle] URLForResource:imageName withExtension:nil] || + [[NSBundle mainBundle] URLForResource:imageName withExtension:@"png"]) { + return YES; + } + + return imageName.pathComponents.count == 1 && !imageName.pathExtension.length; +} + + - (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + NSString *imageName = [self imageNameForRequestURL:imageURL]; + + __block BOOL cancelled = NO; + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelled) { + return; + } + + UIImage *image = [UIImage imageNamed:imageName]; + if (image) { + if (progressHandler) { + progressHandler(1, 1); + } + + if (completionHandler) { + completionHandler(nil, image); + } + } else { + if (completionHandler) { + NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName]; + completionHandler(RCTErrorWithMessage(message), nil); + } + } + }); + + return ^{ + cancelled = YES; + }; +} + +@end diff --git a/Libraries/Image/RCTAssetsLibraryImageLoader.h b/Libraries/Image/RCTAssetsLibraryImageLoader.h new file mode 100644 index 000000000..f075325e1 --- /dev/null +++ b/Libraries/Image/RCTAssetsLibraryImageLoader.h @@ -0,0 +1,28 @@ +/** + * 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 "RCTImageLoader.h" + +@interface RCTAssetsLibraryImageLoader : NSObject + +@end + +@interface RCTBridge (RCTAssetsLibraryImageLoader) + +/** + * The shared Assets Library image loader + */ +@property (nonatomic, readonly) RCTAssetsLibraryImageLoader *assetsLibraryImageLoader; + +/** + * The shared asset library instance. + */ +@property (nonatomic, readonly) ALAssetsLibrary *assetsLibrary; + +@end diff --git a/Libraries/Image/RCTAssetsLibraryImageLoader.m b/Libraries/Image/RCTAssetsLibraryImageLoader.m new file mode 100644 index 000000000..bc6908fed --- /dev/null +++ b/Libraries/Image/RCTAssetsLibraryImageLoader.m @@ -0,0 +1,155 @@ +/** + * 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 "RCTAssetsLibraryImageLoader.h" + +#import +#import +#import + +#import "RCTBridge.h" +#import "RCTConvert.h" +#import "RCTImageLoader.h" +#import "RCTImageUtils.h" +#import "RCTUtils.h" + +static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void); +static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error); + +@implementation RCTAssetsLibraryImageLoader +{ + ALAssetsLibrary *_assetsLibrary; +} + +RCT_EXPORT_MODULE() + +@synthesize bridge = _bridge; + +- (ALAssetsLibrary *)assetsLibrary +{ + return _assetsLibrary ?: (_assetsLibrary = [ALAssetsLibrary new]); +} + +#pragma mark - RCTImageLoader + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return [requestURL.scheme.lowercaseString isEqualToString:@"assets-library"]; +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + [[self assetsLibrary] assetForURL:imageURL resultBlock:^(ALAsset *asset) { + if (asset) { + // ALAssetLibrary API is async and will be multi-threaded. Loading a few full + // resolution images at once will spike the memory up to store the image data, + // and might trigger memory warnings and/or OOM crashes. + // To improve this, process the loaded asset in a serial queue. + dispatch_async(RCTAssetsLibraryImageLoaderQueue(), ^{ + // Also make sure the image is released immediately after it's used so it + // doesn't spike the memory up during the process. + @autoreleasepool { + + BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); + ALAssetRepresentation *representation = [asset defaultRepresentation]; + + UIImage *image; + NSError *error = nil; + if (useMaximumSize) { + image = [UIImage imageWithCGImage:representation.fullResolutionImage + scale:scale + orientation:(UIImageOrientation)representation.orientation]; + } else { + image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error); + } + + completionHandler(error, image); + } + }); + } else { + NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL]; + NSError *error = RCTErrorWithMessage(errorText); + completionHandler(error, nil); + } + } failureBlock:^(NSError *loadError) { + NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError]; + NSError *error = RCTErrorWithMessage(errorText); + completionHandler(error, nil); + }]; + + return ^{}; +} + +@end + +@implementation RCTBridge (RCTAssetsLibraryImageLoader) + +- (RCTAssetsLibraryImageLoader *)assetsLibraryImageLoader +{ + return self.modules[RCTBridgeModuleNameForClass([RCTAssetsLibraryImageLoader class])]; +} + +- (ALAssetsLibrary *)assetsLibrary +{ + return [self.assetsLibraryImageLoader assetsLibrary]; +} + +@end + +static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void) +{ + static dispatch_queue_t queue; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + queue = dispatch_queue_create("com.facebook.RCTAssetsLibraryImageLoader", DISPATCH_QUEUE_SERIAL); + }); + + return queue; +} + +// Why use a custom scaling method? Greater efficiency, reduced memory overhead: +// http://www.mindsea.com/2012/12/downscaling-huge-alassets-without-fear-of-sigkill + +static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error) +{ + NSUInteger length = (NSUInteger)representation.size; + NSMutableData *data = [NSMutableData dataWithLength:length]; + if (![representation getBytes:data.mutableBytes + fromOffset:0 + length:length + error:error]) { + return nil; + } + + CGSize sourceSize = representation.dimensions; + CGSize targetSize = RCTTargetSize(sourceSize, representation.scale, + size, scale, resizeMode, NO); + + NSDictionary *options = @{ + (id)kCGImageSourceShouldAllowFloat: @YES, + (id)kCGImageSourceCreateThumbnailWithTransform: @YES, + (id)kCGImageSourceCreateThumbnailFromImageAlways: @YES, + (id)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * scale) + }; + + CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil); + CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); + if (sourceRef) { + CFRelease(sourceRef); + } + + if (imageRef) { + UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale + orientation:UIImageOrientationUp]; + CGImageRelease(imageRef); + return image; + } + + return nil; +} diff --git a/Libraries/Image/RCTCameraRollManager.m b/Libraries/Image/RCTCameraRollManager.m index e2f2a297f..3d719f4ab 100644 --- a/Libraries/Image/RCTCameraRollManager.m +++ b/Libraries/Image/RCTCameraRollManager.m @@ -14,6 +14,7 @@ #import #import +#import "RCTAssetsLibraryImageLoader.h" #import "RCTBridge.h" #import "RCTImageLoader.h" #import "RCTLog.h" diff --git a/Libraries/Image/RCTGIFImage.h b/Libraries/Image/RCTGIFImageDecoder.h similarity index 56% rename from Libraries/Image/RCTGIFImage.h rename to Libraries/Image/RCTGIFImageDecoder.h index 25533591a..51c25fa2e 100644 --- a/Libraries/Image/RCTGIFImage.h +++ b/Libraries/Image/RCTGIFImageDecoder.h @@ -7,9 +7,8 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import -#import -#import +#import "RCTImageLoader.h" -extern CAKeyframeAnimation *RCTGIFImageWithData(NSData *data); -extern CAKeyframeAnimation *RCTGIFImageWithFileURL(NSURL *URL); +@interface RCTGIFImageDecoder : NSObject + +@end diff --git a/Libraries/Image/RCTGIFImage.m b/Libraries/Image/RCTGIFImageDecoder.m similarity index 74% rename from Libraries/Image/RCTGIFImage.m rename to Libraries/Image/RCTGIFImageDecoder.m index 4258262ed..0419ed728 100644 --- a/Libraries/Image/RCTGIFImage.m +++ b/Libraries/Image/RCTGIFImageDecoder.m @@ -7,16 +7,29 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTGIFImage.h" +#import "RCTGIFImageDecoder.h" -#import "RCTLog.h" +#import +#import +#import -static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSource) +#import "RCTUtils.h" + +@implementation RCTGIFImageDecoder + +RCT_EXPORT_MODULE() + +- (BOOL)canDecodeImageData:(NSData *)imageData { - if (!UTTypeConformsTo(CGImageSourceGetType(imageSource), kUTTypeGIF)) { - return nil; - } + char header[7] = {}; + [imageData getBytes:header length:6]; + return !strcmp(header, "GIF87a") == 0 || !strcmp(header, "GIF89a"); +} + +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL); NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL); NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue]; @@ -48,6 +61,7 @@ static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSou delays[i] = delayTime; images[i] = (__bridge_transfer id)image; } + CFRelease(imageSource); NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count]; NSTimeInterval runningDuration = 0; @@ -64,34 +78,9 @@ static CAKeyframeAnimation *RCTGIFImageWithImageSource(CGImageSourceRef imageSou animation.keyTimes = keyTimes; animation.values = images; animation.duration = duration; - return animation; + completionHandler(nil, animation); + + return nil; } -CAKeyframeAnimation *RCTGIFImageWithData(NSData *data) -{ - if (data.length == 0) { - return nil; - } - - CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)data, NULL); - CAKeyframeAnimation *animation = RCTGIFImageWithImageSource(imageSource); - CFRelease(imageSource); - return animation; -} - -CAKeyframeAnimation *RCTGIFImageWithFileURL(NSURL *URL) -{ - if (!URL) { - return nil; - } - - if (!URL.fileURL) { - RCTLogError(@"Loading remote image URLs synchronously is a really bad idea."); - return nil; - } - - CGImageSourceRef imageSource = CGImageSourceCreateWithURL((CFURLRef)URL, NULL); - CAKeyframeAnimation *animation = RCTGIFImageWithImageSource(imageSource); - CFRelease(imageSource); - return animation; -} +@end diff --git a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj index cbc8c58ee..06029c731 100644 --- a/Libraries/Image/RCTImage.xcodeproj/project.pbxproj +++ b/Libraries/Image/RCTImage.xcodeproj/project.pbxproj @@ -9,8 +9,7 @@ /* Begin PBXBuildFile section */ 1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */; }; 1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; }; - 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; }; - 1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1345A8381B26592900583190 /* RCTImageRequestHandler.m */; }; + 1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; }; 134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; }; 137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; }; 143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; }; @@ -18,6 +17,9 @@ 35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; }; 354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; }; 58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; }; + 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; }; + 8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; }; + 83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -37,10 +39,8 @@ 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageView.m; sourceTree = ""; }; 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageViewManager.h; sourceTree = ""; }; 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageViewManager.m; sourceTree = ""; }; - 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImage.h; sourceTree = ""; }; - 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImage.m; sourceTree = ""; }; - 1345A8371B26592900583190 /* RCTImageRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageRequestHandler.h; sourceTree = ""; }; - 1345A8381B26592900583190 /* RCTImageRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageRequestHandler.m; sourceTree = ""; }; + 1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTGIFImageDecoder.h; sourceTree = ""; }; + 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = ""; }; 134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = ""; }; 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = ""; }; 137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = ""; }; @@ -56,6 +56,12 @@ 58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; }; 58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = ""; }; 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = ""; }; + 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = ""; }; + 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = ""; }; + 8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = ""; }; + 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = ""; }; + 83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetBundleImageLoader.h; sourceTree = ""; }; + 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetBundleImageLoader.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,10 +78,14 @@ 58B511541A9E6B3D00147676 = { isa = PBXGroup; children = ( + 83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */, + 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */, + 8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */, + 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */, 143879331AAD238D00F088A5 /* RCTCameraRollManager.h */, 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */, - 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */, - 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */, + 1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */, + 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */, 58B511891A9E6BD600147676 /* RCTImageDownloader.h */, 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */, 354631661B69857700AA0B86 /* RCTImageEditingManager.h */, @@ -84,8 +94,6 @@ 143879371AAD32A300F088A5 /* RCTImageLoader.m */, 137620331B31C53500677FF0 /* RCTImagePickerManager.h */, 137620341B31C53500677FF0 /* RCTImagePickerManager.m */, - 1345A8371B26592900583190 /* RCTImageRequestHandler.h */, - 1345A8381B26592900583190 /* RCTImageRequestHandler.m */, 1304D5A71AA8C4A30002E2BE /* RCTImageView.h */, 1304D5A81AA8C4A30002E2BE /* RCTImageView.m */, 1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */, @@ -94,6 +102,8 @@ 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */, 134B00A01B54232B00EC8DFB /* RCTImageUtils.h */, 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */, + 8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */, + 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */, 58B5115E1A9E6B3D00147676 /* Products */, ); indentWidth = 2; @@ -164,17 +174,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */, 35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */, + 8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */, 58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */, 137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */, 1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */, - 1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */, - 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */, + 1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */, 143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */, 143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */, 354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */, 1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */, 134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */, + 83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Libraries/Image/RCTImageDownloader.h b/Libraries/Image/RCTImageDownloader.h index c231162c4..0e5c8de0b 100644 --- a/Libraries/Image/RCTImageDownloader.h +++ b/Libraries/Image/RCTImageDownloader.h @@ -7,25 +7,10 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import - #import "RCTBridge.h" #import "RCTImageLoader.h" -@interface RCTImageDownloader : NSObject - -/** - * Downloads an image and decompresses it a the size specified. The compressed - * image will be cached in memory and to disk. Note that the callback block - * will not be executed on the same thread you called the method from, nor on - * the main thread. Returns a token that can be used to cancel the download. - */ -- (RCTImageLoaderCancellationBlock)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - progressBlock:(RCTImageLoaderProgressBlock)progressBlock - completionBlock:(RCTImageLoaderCompletionBlock)block; +@interface RCTImageDownloader : NSObject @end diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index 22bbe9031..3d28983d2 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -9,7 +9,7 @@ #import "RCTImageDownloader.h" -#import "RCTGIFImage.h" +#import "RCTImageLoader.h" #import "RCTImageUtils.h" #import "RCTLog.h" #import "RCTNetworking.h" @@ -25,16 +25,6 @@ RCT_EXPORT_MODULE() -+ (RCTImageDownloader *)sharedInstance -{ - static RCTImageDownloader *sharedInstance; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [RCTImageDownloader new]; - }); - return sharedInstance; -} - - (instancetype)init { if ((self = [super init])) { @@ -44,14 +34,19 @@ RCT_EXPORT_MODULE() return self; } +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return [requestURL.scheme.lowercaseString hasPrefix:@"http"]; +} + /** * Downloads a block of raw data and returns it. Note that the callback block * will not be executed on the same thread you called the method from, nor on * the main thread. Returns a token that can be used to cancel the download. */ - (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url - progressBlock:(RCTImageLoaderProgressBlock)progressBlock - completionBlock:(RCTImageLoaderCompletionBlock)completionBlock + progressHandler:(RCTImageLoaderProgressBlock)progressBlock + completionHandler:(RCTImageLoaderCompletionBlock)completionBlock { if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) { RCTLogError(@"You need to import the RCTNetworking library in order to download remote images."); @@ -99,49 +94,31 @@ RCT_EXPORT_MODULE() return ^{ [task cancel]; }; } -- (RCTImageLoaderCancellationBlock)downloadImageForURL:(NSURL *)url - size:(CGSize)size - scale:(CGFloat)scale - resizeMode:(UIViewContentMode)resizeMode - progressBlock:(RCTImageLoaderProgressBlock)progressBlock - completionBlock:(RCTImageLoaderCompletionBlock)completionBlock +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + progressHandler:(RCTImageLoaderProgressBlock)progressHandler + completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { - scale = scale ?: RCTScreenScale(); + __block RCTImageLoaderCancellationBlock decodeCancel = nil; - return [self downloadDataForURL:url progressBlock:progressBlock completionBlock:^(NSError *error, id data) { - - if (!data || error) { - completionBlock(error, nil); - return; + __weak RCTImageDownloader *weakSelf = self; + RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) { + if (error) { + completionHandler(error, nil); + } else { + decodeCancel = [weakSelf.bridge.imageLoader decodeImageData:imageData size:size scale:scale resizeMode:resizeMode completionBlock:completionHandler]; } - - if ([url.path.lowercaseString hasSuffix:@".gif"]) { - id image = RCTGIFImageWithData(data); - if (!image && !error) { - NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", url]; - error = RCTErrorWithMessage(errorMessage); - } - completionBlock(error, image); - return; - } - - UIImage *image = [UIImage imageWithData:data scale:scale]; - if (image && !CGSizeEqualToSize(size, CGSizeZero)) { - - // Get destination size - CGSize targetSize = RCTTargetSize(image.size, image.scale, - size, scale, resizeMode, NO); - - // Decompress image at required size - BOOL opaque = !RCTImageHasAlpha(image.CGImage); - UIGraphicsBeginImageContextWithOptions(targetSize, opaque, scale); - [image drawInRect:(CGRect){CGPointZero, targetSize}]; - image = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - } - - completionBlock(nil, image); }]; + + return ^{ + downloadCancel(); + + if (decodeCancel) { + decodeCancel(); + } + }; } @end diff --git a/Libraries/Image/RCTImageLoader.h b/Libraries/Image/RCTImageLoader.h index bde29ec40..bba887602 100644 --- a/Libraries/Image/RCTImageLoader.h +++ b/Libraries/Image/RCTImageLoader.h @@ -10,14 +10,16 @@ #import #import "RCTBridge.h" +#import "RCTURLRequestHandler.h" @class ALAssetsLibrary; typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total); typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* UIImage or CAAnimation */); +typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* NSData, UIImage, CAAnimation */); typedef void (^RCTImageLoaderCancellationBlock)(void); -@interface RCTImageLoader : NSObject +@interface RCTImageLoader : NSObject /** * Loads the specified image at the highest available resolution. @@ -38,14 +40,14 @@ typedef void (^RCTImageLoaderCancellationBlock)(void); completionBlock:(RCTImageLoaderCompletionBlock)completionBlock; /** - * Is the specified image tag an asset library image? + * Finds an appropriate image decoder and passes the target size, scale and + * resizeMode for optimal image decoding. */ -+ (BOOL)isAssetLibraryImage:(NSString *)imageTag; - -/** - * Is the specified image tag a remote image? - */ -+ (BOOL)isRemoteImage:(NSString *)imageTag; +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + completionBlock:(RCTImageLoaderCompletionBlock)completionBlock; @end @@ -56,9 +58,72 @@ typedef void (^RCTImageLoaderCancellationBlock)(void); */ @property (nonatomic, readonly) RCTImageLoader *imageLoader; +@end + /** - * The shared asset library instance. + * Provides the interface needed to register an image data loader. Image data + * loaders are also bridge modules, so should be registered using + * RCT_EXPORT_MODULE(). */ -@property (nonatomic, readonly) ALAssetsLibrary *assetsLibrary; +@protocol RCTImageURLLoader + +/** + * Indicates whether this data loader is capable of processing the specified + * request URL. Typically the handler would examine the scheme/protocol of the + * URL to determine this. + */ +- (BOOL)canLoadImageURL:(NSURL *)requestURL; + +/** + * Send a network request to load the request URL. The method should call the + * progressHandler (if applicable) and the completionHandler when the request + * has finished. The method should also return a cancellation block, if + * applicable. + */ +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler; + +@optional + +/** + * If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:` + * then `imageLoaderPriority` is used to determine which one to use. The handler + * with the highest priority will be selected. Default priority is zero. If + * two or more valid handlers have the same priority, the selection order is + * undefined. + */ +- (float)imageLoaderPriority; + +@end + +/** + * Provides the interface needed to register an image decoder. Image decoders + * are also bridge modules, so should be registered using RCT_EXPORT_MODULE(). + */ +@protocol RCTImageDecoder + +/** + * Indicates whether this handler is capable of decoding the specified data. + * Typically the handler would examine some sort of header data to determine + * this. + */ +- (BOOL)canDecodeImageData:(NSData *)imageData; + +/** + * Decode an image from the data object. The method should call the + * completionHandler when the decoding operation has finished. The method + * should also return a cancellation block, if applicable. + */ +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler; + +@optional + +/** + * If more than one RCTImageDecoder responds YES to `-canDecodeImageData:` + * then `imageDecoderPriority` is used to determine which one to use. The + * handler with the highest priority will be selected. Default priority is zero. + * If two or more valid handlers have the same priority, the selection order is + * undefined. + */ +- (float)imageDecoderPriority; @end diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index a72e01ef2..ba4ac7ea1 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -9,19 +9,11 @@ #import "RCTImageLoader.h" -#import -#import -#import -#import #import -#import "RCTBridge.h" #import "RCTConvert.h" #import "RCTDefines.h" -#import "RCTGIFImage.h" #import "RCTImageDownloader.h" -#import "RCTImageStoreManager.h" -#import "RCTImageUtils.h" #import "RCTLog.h" #import "RCTUtils.h" @@ -36,21 +28,7 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr } } -static dispatch_queue_t RCTImageLoaderQueue(void) -{ - static dispatch_queue_t queue = NULL; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - queue = dispatch_queue_create("com.facebook.rctImageLoader", DISPATCH_QUEUE_SERIAL); - }); - - return queue; -} - @implementation RCTImageLoader -{ - ALAssetsLibrary *_assetsLibrary; -} @synthesize bridge = _bridge; @@ -67,56 +45,33 @@ RCT_EXPORT_MODULE() completionBlock:callback]; } -// Why use a custom scaling method? Greater efficiency, reduced memory overhead: -// http://www.mindsea.com/2012/12/downscaling-huge-alassets-without-fear-of-sigkill - -static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, - CGSize size, CGFloat scale, - UIViewContentMode resizeMode, - NSError **error) +- (id)imageURLLoaderForRequest:(NSURL *)requestURL { - NSUInteger length = (NSUInteger)representation.size; - NSMutableData *data = [NSMutableData dataWithLength:length]; - if (![representation getBytes:data.mutableBytes - fromOffset:0 - length:length - error:error]) { - return nil; + NSMutableArray *handlers = [NSMutableArray array]; + for (id module in _bridge.modules.allValues) { + if ([module conformsToProtocol:@protocol(RCTImageURLLoader)]) { + if ([(id)module canLoadImageURL:requestURL]) { + [handlers addObject:module]; + } + } } + [handlers sortUsingComparator:^NSComparisonResult(id a, id b) { + float priorityA = [a respondsToSelector:@selector(imageLoaderPriority)] ? [a imageLoaderPriority] : 0; + float priorityB = [b respondsToSelector:@selector(imageLoaderPriority)] ? [b imageLoaderPriority] : 0; + if (priorityA < priorityB) { + return NSOrderedAscending; + } else if (priorityA > priorityB) { + return NSOrderedDescending; + } else { + RCTLogError(@"The RCTImageLoader %@ and %@ both reported that they can" + " handle the load request %@, and have equal priority (%g)." + " This could result in non-deterministic behavior.", + a, b, requestURL, priorityA); - CGSize sourceSize = representation.dimensions; - CGSize targetSize = RCTTargetSize(sourceSize, representation.scale, - size, scale, resizeMode, NO); - - NSDictionary *options = @{ - (id)kCGImageSourceShouldAllowFloat: @YES, - (id)kCGImageSourceCreateThumbnailWithTransform: @YES, - (id)kCGImageSourceCreateThumbnailFromImageAlways: @YES, - (id)kCGImageSourceThumbnailMaxPixelSize: @(MAX(targetSize.width, targetSize.height) * scale) - }; - - CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil); - CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(sourceRef, 0, (__bridge CFDictionaryRef)options); - if (sourceRef) { - CFRelease(sourceRef); - } - - if (imageRef) { - UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale - orientation:UIImageOrientationUp]; - CGImageRelease(imageRef); - return image; - } - - return nil; -} - -- (ALAssetsLibrary *)assetsLibrary -{ - if (!_assetsLibrary) { - _assetsLibrary = [ALAssetsLibrary new]; - } - return _assetsLibrary; + return NSOrderedSame; + } + }]; + return [handlers lastObject]; } - (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag @@ -126,141 +81,122 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, progressBlock:(RCTImageLoaderProgressBlock)progressBlock completionBlock:(RCTImageLoaderCompletionBlock)completionBlock { - if ([imageTag hasPrefix:@"assets-library://"]) { - [[self assetsLibrary] assetForURL:[RCTConvert NSURL:imageTag] resultBlock:^(ALAsset *asset) { - if (asset) { - // ALAssetLibrary API is async and will be multi-threaded. Loading a few full - // resolution images at once will spike the memory up to store the image data, - // and might trigger memory warnings and/or OOM crashes. - // To improve this, process the loaded asset in a serial queue. - dispatch_async(RCTImageLoaderQueue(), ^{ - // Also make sure the image is released immediately after it's used so it - // doesn't spike the memory up during the process. - @autoreleasepool { + NSURL *requestURL = [RCTConvert NSURL:imageTag]; + id loadHandler = [self imageURLLoaderForRequest:requestURL]; + if (!loadHandler) { + RCTLogError(@"No suitable image URL loader found for %@", imageTag); + } - BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); - ALAssetRepresentation *representation = [asset defaultRepresentation]; + return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:progressBlock completionHandler:^(NSError *error, id image) { + RCTDispatchCallbackOnMainQueue(completionBlock, error, image); + }]; +} - UIImage *image; - NSError *error = nil; - if (useMaximumSize) { - image = [UIImage imageWithCGImage:representation.fullResolutionImage - scale:scale - orientation:(UIImageOrientation)representation.orientation]; - } else { - image = RCTScaledImageForAsset(representation, size, scale, resizeMode, &error); - } - - RCTDispatchCallbackOnMainQueue(completionBlock, error, image); - } - }); - } else { - NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageTag]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); +- (id)imageDecoderForRequest:(NSData *)imageData +{ + NSMutableArray *handlers = [NSMutableArray array]; + for (id module in _bridge.modules.allValues) { + if ([module conformsToProtocol:@protocol(RCTImageDecoder)]) { + if ([(id)module canDecodeImageData:imageData]) { + [handlers addObject:module]; } - } failureBlock:^(NSError *loadError) { - NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageTag, loadError]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - }]; - return ^{}; - } else if ([imageTag hasPrefix:@"ph://"]) { - // Using PhotoKit for iOS 8+ - // The 'ph://' prefix is used by FBMediaKit to differentiate between - // assets-library. It is prepended to the local ID so that it is in the - // form of an, NSURL which is what assets-library uses. - NSString *phAssetID = [imageTag substringFromIndex:@"ph://".length]; - PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil]; - if (results.count == 0) { - NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - return ^{}; } - - PHAsset *asset = results.firstObject; - - PHImageRequestOptions *imageOptions = [PHImageRequestOptions new]; - - BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); - CGSize targetSize; - - if ( useMaximumSize ){ - targetSize = PHImageManagerMaximumSize; - imageOptions.resizeMode = PHImageRequestOptionsResizeModeNone; + } + [handlers sortUsingComparator:^NSComparisonResult(id a, id b) { + float priorityA = [a respondsToSelector:@selector(imageDecoderPriority)] ? [a imageDecoderPriority] : 0; + float priorityB = [b respondsToSelector:@selector(imageDecoderPriority)] ? [b imageDecoderPriority] : 0; + if (priorityA < priorityB) { + return NSOrderedAscending; + } else if (priorityA > priorityB) { + return NSOrderedDescending; } else { - targetSize = size; - imageOptions.resizeMode = PHImageRequestOptionsResizeModeFast; - } + RCTLogError(@"The RCTImageDecoder %@ and %@ both reported that they can" + " handle the decode request , and have" + " equal priority (%g). This could result in" + " non-deterministic behavior.", + a, b, imageData, imageData.length, priorityA); - PHImageContentMode contentMode = PHImageContentModeAspectFill; - if (resizeMode == UIViewContentModeScaleAspectFit) { - contentMode = PHImageContentModeAspectFit; + return NSOrderedSame; } - [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:imageOptions resultHandler:^(UIImage *result, NSDictionary *info) { - if (result) { - RCTDispatchCallbackOnMainQueue(completionBlock, nil, result); - } else { - NSString *errorText = [NSString stringWithFormat:@"Failed to load PHAsset with local identifier %@ with no error message.", phAssetID]; - NSError *error = RCTErrorWithMessage(errorText); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - return; - } - }]; - return ^{}; - } else if ([imageTag hasPrefix:@"http"]) { - NSURL *url = [RCTConvert NSURL:imageTag]; - if (!url) { - NSString *errorMessage = [NSString stringWithFormat:@"Invalid URL: %@", imageTag]; - RCTDispatchCallbackOnMainQueue(completionBlock, RCTErrorWithMessage(errorMessage), nil); - return ^{}; - } - return [_bridge.imageDownloader downloadImageForURL:url size:size scale:scale resizeMode:resizeMode progressBlock:progressBlock completionBlock:^(NSError *error, id image) { + }]; + return [handlers lastObject]; +} + +- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data + size:(CGSize)size + scale:(CGFloat)scale + resizeMode:(UIViewContentMode)resizeMode + completionBlock:(RCTImageLoaderCompletionBlock)completionBlock +{ + id imageDecoder = [self imageDecoderForRequest:data]; + if (imageDecoder) { + return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, id image) { RCTDispatchCallbackOnMainQueue(completionBlock, error, image); }]; - } else if ([imageTag hasPrefix:@"rct-image-store://"]) { - [_bridge.imageStoreManager getImageForTag:imageTag withBlock:^(UIImage *image) { + } else { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + UIImage *image = [UIImage imageWithData:data]; if (image) { RCTDispatchCallbackOnMainQueue(completionBlock, nil, image); } else { - NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag]; - NSError *error = RCTErrorWithMessage(errorMessage); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); + NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data ", data, data.length]; + NSError *finalError = RCTErrorWithMessage(errorMessage); + RCTDispatchCallbackOnMainQueue(completionBlock, finalError, nil); } - }]; - return ^{}; - } else if ([imageTag.lowercaseString hasSuffix:@".gif"]) { - id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]); - if (image) { - RCTDispatchCallbackOnMainQueue(completionBlock, nil, image); - } else { - NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag]; - NSError *error = RCTErrorWithMessage(errorMessage); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - } - return ^{}; - } else { - UIImage *image = [RCTConvert UIImage:imageTag]; - if (image) { - RCTDispatchCallbackOnMainQueue(completionBlock, nil, image); - } else { - NSString *errorMessage = [NSString stringWithFormat:@"Unrecognized tag protocol: %@", imageTag]; - NSError *error = RCTErrorWithMessage(errorMessage); - RCTDispatchCallbackOnMainQueue(completionBlock, error, nil); - } + }); return ^{}; } } -+ (BOOL)isAssetLibraryImage:(NSString *)imageTag +#pragma mark - RCTURLRequestHandler + +- (BOOL)canHandleRequest:(NSURLRequest *)request { - return [imageTag hasPrefix:@"assets-library://"] || [imageTag hasPrefix:@"ph://"]; + id handler = [self imageURLLoaderForRequest:request.URL]; + + // RCTImageDownloader is an image plugin that uses the networking stack. + // We don't want to route any network calls through the image downloader + // as that would cause cyclical dependencies. + return handler && ![handler isKindOfClass:[RCTImageDownloader class]]; } -+ (BOOL)isRemoteImage:(NSString *)imageTag +- (id)sendRequest:(NSURLRequest *)request withDelegate:(id)delegate { - return [imageTag hasPrefix:@"http://"] || [imageTag hasPrefix:@"https://"]; + __block RCTImageLoaderCancellationBlock requestToken; + requestToken = [self.bridge.imageLoader loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) { + if (error) { + [delegate URLRequest:requestToken didCompleteWithError:error]; + return; + } + + NSString *mimeType = nil; + NSData *imageData = nil; + if (RCTImageHasAlpha(image.CGImage)) { + mimeType = @"image/png"; + imageData = UIImagePNGRepresentation(image); + } else { + mimeType = @"image/jpeg"; + imageData = UIImageJPEGRepresentation(image, 1.0); + } + + NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL + MIMEType:mimeType + expectedContentLength:imageData.length + textEncodingName:nil]; + + [delegate URLRequest:requestToken didReceiveResponse:response]; + [delegate URLRequest:requestToken didReceiveData:imageData]; + [delegate URLRequest:requestToken didCompleteWithError:nil]; + }]; + + return requestToken; +} + +- (void)cancelRequest:(id)requestToken +{ + if (requestToken) { + ((RCTImageLoaderCancellationBlock)requestToken)(); + } } @end @@ -272,9 +208,4 @@ static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, return self.modules[RCTBridgeModuleNameForClass([RCTImageLoader class])]; } -- (ALAssetsLibrary *)assetsLibrary -{ - return [self.imageLoader assetsLibrary]; -} - @end diff --git a/Libraries/Image/RCTImageRequestHandler.m b/Libraries/Image/RCTImageRequestHandler.m deleted file mode 100644 index 1b3d76df1..000000000 --- a/Libraries/Image/RCTImageRequestHandler.m +++ /dev/null @@ -1,70 +0,0 @@ -// -// RCTImageRequestHandler.m -// RCTImage -// -// Created by Nick Lockwood on 09/06/2015. -// Copyright (c) 2015 Facebook. All rights reserved. -// - -#import "RCTImageRequestHandler.h" - -#import - -#import "RCTBridge.h" -#import "RCTImageLoader.h" -#import "RCTUtils.h" - -@implementation RCTImageRequestHandler - -RCT_EXPORT_MODULE() - -@synthesize bridge = _bridge; - -- (BOOL)canHandleRequest:(NSURLRequest *)request -{ - return [@[@"assets-library", @"ph"] containsObject:request.URL.scheme.lowercaseString]; -} - -- (id)sendRequest:(NSURLRequest *)request - withDelegate:(id)delegate -{ - NSString *URLString = request.URL.absoluteString; - - __block RCTImageLoaderCancellationBlock requestToken = nil; - requestToken = [_bridge.imageLoader loadImageWithTag:URLString callback:^(NSError *error, UIImage *image) { - if (error) { - [delegate URLRequest:requestToken didCompleteWithError:error]; - return; - } - - NSString *mimeType = nil; - NSData *imageData = nil; - if (RCTImageHasAlpha(image.CGImage)) { - mimeType = @"image/png"; - imageData = UIImagePNGRepresentation(image); - } else { - mimeType = @"image/jpeg"; - imageData = UIImageJPEGRepresentation(image, 1.0); - } - - NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL - MIMEType:mimeType - expectedContentLength:imageData.length - textEncodingName:nil]; - - [delegate URLRequest:requestToken didReceiveResponse:response]; - [delegate URLRequest:requestToken didReceiveData:imageData]; - [delegate URLRequest:requestToken didCompleteWithError:nil]; - }]; - - return requestToken; -} - -- (void)cancelRequest:(id /* RCTImageLoaderCancellationBlock */)requestToken -{ - if (requestToken) { - ((RCTImageLoaderCancellationBlock)requestToken)(); - } -} - -@end diff --git a/Libraries/Image/RCTImageStoreManager.h b/Libraries/Image/RCTImageStoreManager.h index afb4f24cb..5f6a1cd64 100644 --- a/Libraries/Image/RCTImageStoreManager.h +++ b/Libraries/Image/RCTImageStoreManager.h @@ -3,9 +3,10 @@ #import #import "RCTBridge.h" +#import "RCTImageLoader.h" #import "RCTURLRequestHandler.h" -@interface RCTImageStoreManager : NSObject +@interface RCTImageStoreManager : NSObject /** * Set and get cached images. These must be called from the main thread. diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index 3ff66ce59..c7f5a9fd8 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -101,7 +101,7 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String - (BOOL)canHandleRequest:(NSURLRequest *)request { - return [@[@"rct-image-store"] containsObject:request.URL.scheme.lowercaseString]; + return [request.URL.scheme.lowercaseString isEqualToString:@"rct-image-store"]; } - (id)sendRequest:(NSURLRequest *)request @@ -137,6 +137,29 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String return request; } +#pragma mark - RCTImageLoader + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return [requestURL.scheme.lowercaseString isEqualToString:@"rct-image-store"]; +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + NSString *imageTag = imageURL.absoluteString; + [self getImageForTag:imageTag withBlock:^(UIImage *image) { + if (image) { + completionHandler(nil, image); + } else { + NSString *errorMessage = [NSString stringWithFormat:@"Unable to load image from image store: %@", imageTag]; + NSError *error = RCTErrorWithMessage(errorMessage); + completionHandler(error, nil); + } + }]; + + return nil; +} + @end @implementation RCTBridge (RCTImageStoreManager) diff --git a/Libraries/Image/RCTImageView.m b/Libraries/Image/RCTImageView.m index db67ed44a..01e5b2fbe 100644 --- a/Libraries/Image/RCTImageView.m +++ b/Libraries/Image/RCTImageView.m @@ -12,7 +12,6 @@ #import "RCTBridge.h" #import "RCTConvert.h" #import "RCTEventDispatcher.h" -#import "RCTGIFImage.h" #import "RCTImageLoader.h" #import "RCTImageUtils.h" #import "RCTUtils.h" @@ -101,11 +100,20 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) } } ++ (BOOL)srcNeedsReload:(NSString *)src +{ + return + [src hasPrefix:@"http://"] || + [src hasPrefix:@"https://"] || + [src hasPrefix:@"assets-library://"] || + [src hasPrefix:@"ph://"]; +} + - (void)setContentMode:(UIViewContentMode)contentMode { if (self.contentMode != contentMode) { super.contentMode = contentMode; - if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) { + if ([RCTImageView srcNeedsReload:_src]) { [self reloadImage]; } } @@ -166,7 +174,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init) [super reactSetFrame:frame]; if (self.image == nil) { [self reloadImage]; - } else if ([RCTImageLoader isAssetLibraryImage:_src] || [RCTImageLoader isRemoteImage:_src]) { + } else if ([RCTImageView srcNeedsReload:_src]) { // Get optimal image size CGSize currentSize = self.image.size; diff --git a/Libraries/Image/RCTPhotoLibraryImageLoader.h b/Libraries/Image/RCTPhotoLibraryImageLoader.h new file mode 100644 index 000000000..33cbab3f0 --- /dev/null +++ b/Libraries/Image/RCTPhotoLibraryImageLoader.h @@ -0,0 +1,14 @@ +/** + * 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 "RCTImageLoader.h" + +@interface RCTPhotoLibraryImageLoader : NSObject + +@end diff --git a/Libraries/Image/RCTPhotoLibraryImageLoader.m b/Libraries/Image/RCTPhotoLibraryImageLoader.m new file mode 100644 index 000000000..144f80805 --- /dev/null +++ b/Libraries/Image/RCTPhotoLibraryImageLoader.m @@ -0,0 +1,82 @@ +/** + * 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 "RCTPhotoLibraryImageLoader.h" + +#import + +#import "RCTImageUtils.h" +#import "RCTUtils.h" + +@implementation RCTPhotoLibraryImageLoader + +RCT_EXPORT_MODULE() + +@synthesize bridge = _bridge; + +#pragma mark - RCTImageLoader + +- (BOOL)canLoadImageURL:(NSURL *)requestURL +{ + return [requestURL.scheme.lowercaseString isEqualToString:@"ph"]; +} + +- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler +{ + // Using PhotoKit for iOS 8+ + // The 'ph://' prefix is used by FBMediaKit to differentiate between + // assets-library. It is prepended to the local ID so that it is in the + // form of an, NSURL which is what assets-library uses. + NSString *phAssetID = [imageURL.absoluteString substringFromIndex:[@"ph://" length]]; + PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil]; + if (results.count == 0) { + NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID]; + NSError *error = RCTErrorWithMessage(errorText); + completionHandler(error, nil); + return ^{}; + } + + PHAsset *asset = [results firstObject]; + + PHImageRequestOptions *imageOptions = [PHImageRequestOptions new]; + imageOptions.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { + static const double multiplier = 1e6; + progressHandler(progress * multiplier, multiplier); + }; + + BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero); + CGSize targetSize; + + if (useMaximumSize) { + targetSize = PHImageManagerMaximumSize; + imageOptions.resizeMode = PHImageRequestOptionsResizeModeNone; + } else { + targetSize = size; + imageOptions.resizeMode = PHImageRequestOptionsResizeModeFast; + } + + PHImageContentMode contentMode = PHImageContentModeAspectFill; + if (resizeMode == UIViewContentModeScaleAspectFit) { + contentMode = PHImageContentModeAspectFit; + } + + PHImageRequestID requestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:imageOptions resultHandler:^(UIImage *result, NSDictionary *info) { + if (result) { + completionHandler(nil, result); + } else { + completionHandler(info[PHImageErrorKey], nil); + } + }]; + + return ^{ + [[PHImageManager defaultManager] cancelImageRequest:requestID]; + }; +} + +@end From e092c61bac609069f9095385b3ab07cbe229a36b Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Wed, 2 Sep 2015 09:09:16 -0700 Subject: [PATCH 20/27] Add KeyboardAvoidingView --- React/Base/RCTKeyboardObserver.m | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/React/Base/RCTKeyboardObserver.m b/React/Base/RCTKeyboardObserver.m index 5b0174cdf..fbe4e4f76 100644 --- a/React/Base/RCTKeyboardObserver.m +++ b/React/Base/RCTKeyboardObserver.m @@ -62,7 +62,7 @@ IMPLEMENT_KEYBOARD_HANDLER(keyboardDidChangeFrame) @end -static NSDictionary *RCTRectDictionaryValue(CGRect rect) +NS_INLINE NSDictionary *RCTRectDictionaryValue(CGRect rect) { return @{ @"screenX": @(rect.origin.x), @@ -72,16 +72,34 @@ static NSDictionary *RCTRectDictionaryValue(CGRect rect) }; } +static NSString *RCTAnimationNameForCurve(UIViewAnimationCurve curve) +{ + switch (curve) { + case UIViewAnimationCurveEaseIn: + return @"easeIn"; + case UIViewAnimationCurveEaseInOut: + return @"easeInEaseOut"; + case UIViewAnimationCurveEaseOut: + return @"easeOut"; + case UIViewAnimationCurveLinear: + return @"linear"; + default: + return @"keyboard"; + } +} + static NSDictionary *RCTParseKeyboardNotification(NSNotification *notification) { NSDictionary *userInfo = notification.userInfo; CGRect beginFrame = [userInfo[UIKeyboardFrameBeginUserInfoKey] CGRectValue]; CGRect endFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue]; NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue]; + UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue]; return @{ @"startCoordinates": RCTRectDictionaryValue(beginFrame), @"endCoordinates": RCTRectDictionaryValue(endFrame), @"duration": @(duration * 1000.0), // ms + @"easing": RCTAnimationNameForCurve(curve), }; } From 7f65971761d911d855da67373b9c75e56a221022 Mon Sep 17 00:00:00 2001 From: James Ide Date: Wed, 2 Sep 2015 11:27:15 -0700 Subject: [PATCH 21/27] [npm] Update to stacktrace-parser@0.1.3 and ws@0.8.0 to satisfy io.js 3 / Node 4 Summary: The previous version of stacktrace-parser 0.1.2 had an line in its package.json file that required Node <= 2.x. That line was removed, so it no longer warns on io.js 3.x or Node 4.x. Similarly, ws 0.8.0 was published with support for the new V8, so it compiles with io.js 3.x and therefore should work with Node 4.x. Updated the Travis file as well to run on io.js 3.x. Fixes #2258, #2455 Closes https://github.com/facebook/react-native/pull/2398 Github Author: James Ide --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0cd24e7f7..cebee4dfa 100644 --- a/package.json +++ b/package.json @@ -69,12 +69,12 @@ "sane": "^1.1.2", "semver": "^4.3.6", "source-map": "0.1.31", - "stacktrace-parser": "frantic/stacktrace-parser#493c5e5638", + "stacktrace-parser": "0.1.3", "uglify-js": "2.4.16", "underscore": "1.7.0", "wordwrap": "^1.0.0", "worker-farm": "^1.3.1", - "ws": "^0.7.2", + "ws": "0.8.0", "yargs": "1.3.2", "yeoman-environment": "^1.2.7", "yeoman-generator": "^0.20.2" From c0690354792a49cff6c2e236f5f8a98aaed33b4e Mon Sep 17 00:00:00 2001 From: Alex Kotliarskyi Date: Wed, 2 Sep 2015 13:51:53 -0700 Subject: [PATCH 22/27] [React Native] Bring back .sourceURL --- React/Modules/RCTSourceCode.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/React/Modules/RCTSourceCode.m b/React/Modules/RCTSourceCode.m index 05860e1dd..685ae611a 100644 --- a/React/Modules/RCTSourceCode.m +++ b/React/Modules/RCTSourceCode.m @@ -34,4 +34,10 @@ RCT_EXPORT_METHOD(getScriptText:(RCTResponseSenderBlock)successCallback } } +- (NSDictionary *)constantsToExport +{ + NSString *URL = self.bridge.bundleURL.absoluteString ?: @""; + return @{@"scriptURL": URL}; +} + @end From 62f9059f85368fd5b36045a5b4104051a46f0e82 Mon Sep 17 00:00:00 2001 From: Amjad Masad Date: Wed, 2 Sep 2015 15:40:26 -0700 Subject: [PATCH 23/27] [react-packager] Sourcemap url should include the same args as the bundle url Summary: Sourcemap urls were generated as just the pathname (no options) which meant that they generated source for the wrong bundle. Even worse, there exists a race condition when multiple request to the same bundle has different types of paltform arguments (in this case one could be 'ios' and the other is undefined). The fix will this will come later as it's more involved -- will need to refactor the dependency resolver to have a per-request state. --- packager/react-packager/src/Server/__tests__/Server-test.js | 4 ++-- packager/react-packager/src/Server/index.js | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packager/react-packager/src/Server/__tests__/Server-test.js b/packager/react-packager/src/Server/__tests__/Server-test.js index 9e0a6890c..5152054ee 100644 --- a/packager/react-packager/src/Server/__tests__/Server-test.js +++ b/packager/react-packager/src/Server/__tests__/Server-test.js @@ -125,7 +125,7 @@ describe('processRequest', () => { expect(Bundler.prototype.bundle).toBeCalledWith( 'index.js', true, - 'index.map', + 'index.map?platform=ios', true, 'ios', ); @@ -270,7 +270,7 @@ describe('processRequest', () => { expect(Bundler.prototype.bundle).toBeCalledWith( 'path/to/foo.js', false, - '/path/to/foo.map', + '/path/to/foo.map?dev=false&runModule=false', false, undefined ); diff --git a/packager/react-packager/src/Server/index.js b/packager/react-packager/src/Server/index.js index 1ad6d862b..d727fd52a 100644 --- a/packager/react-packager/src/Server/index.js +++ b/packager/react-packager/src/Server/index.js @@ -429,8 +429,11 @@ class Server { return true; }).join('.') + '.js'; + const sourceMapUrlObj = _.clone(urlObj); + sourceMapUrlObj.pathname = pathname.replace(/\.bundle$/, '.map'); + return { - sourceMapUrl: pathname.replace(/\.bundle$/, '.map'), + sourceMapUrl: url.format(sourceMapUrlObj), entryFile: entryFile, dev: this._getBoolOptionFromQuery(urlObj.query, 'dev', true), minify: this._getBoolOptionFromQuery(urlObj.query, 'minify'), From f28255ea3eaf73c253a5ec1a2c485a9651912a79 Mon Sep 17 00:00:00 2001 From: "Oliver (Cunpu) Bo" Date: Wed, 2 Sep 2015 16:28:04 -0700 Subject: [PATCH 24/27] Support "file://" in RCTImageLoader --- Libraries/Image/RCTImageDownloader.m | 64 +++++++++++++++++++++------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index 3d28983d2..82f777895 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -36,7 +36,9 @@ RCT_EXPORT_MODULE() - (BOOL)canLoadImageURL:(NSURL *)requestURL { - return [requestURL.scheme.lowercaseString hasPrefix:@"http"]; + // Have to exclude 'file://' from the main bundle, otherwise this would conflict with RCTAssetBundleImageLoader + return [requestURL.scheme.lowercaseString hasPrefix:@"http"] || + ([requestURL.scheme.lowercaseString hasPrefix:@"file"] && ![requestURL.path hasPrefix:[NSBundle mainBundle].resourcePath]); } /** @@ -101,24 +103,54 @@ RCT_EXPORT_MODULE() progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { - __block RCTImageLoaderCancellationBlock decodeCancel = nil; + if ([imageURL.scheme isEqualToString:@"http"]) { + __block RCTImageLoaderCancellationBlock decodeCancel = nil; - __weak RCTImageDownloader *weakSelf = self; - RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) { - if (error) { - completionHandler(error, nil); - } else { - decodeCancel = [weakSelf.bridge.imageLoader decodeImageData:imageData size:size scale:scale resizeMode:resizeMode completionBlock:completionHandler]; - } - }]; + __weak RCTImageDownloader *weakSelf = self; + RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) { + if (error) { + completionHandler(error, nil); + } else { + decodeCancel = [weakSelf.bridge.imageLoader decodeImageData:imageData size:size scale:scale resizeMode:resizeMode completionBlock:completionHandler]; + } + }]; - return ^{ - downloadCancel(); + return ^{ + downloadCancel(); - if (decodeCancel) { - decodeCancel(); - } - }; + if (decodeCancel) { + decodeCancel(); + } + }; + } else if ([imageURL.scheme isEqualToString:@"file"]) { + __block BOOL cancelled = NO; + dispatch_async(dispatch_get_main_queue(), ^{ + if (cancelled) { + return; + } + + UIImage *image = [UIImage imageWithContentsOfFile:imageURL.resourceSpecifier]; + if (image) { + if (progressHandler) { + progressHandler(1, 1); + } + if (completionHandler) { + completionHandler(nil, image); + } + } else { + if (completionHandler) { + NSString *message = [NSString stringWithFormat:@"Could not find image at path: %@", imageURL.absoluteString]; + completionHandler(RCTErrorWithMessage(message), nil); + } + } + }); + return ^{ + cancelled = YES; + }; + } else { + RCTLogError(@"Unexpected image schema %@", imageURL.scheme); + return ^{}; + } } @end From aa62a5e4e016b67dc3f7dc902a18edbc88c867c4 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Thu, 3 Sep 2015 03:36:32 -0700 Subject: [PATCH 25/27] Send layout events on shadow queue Summary: We currently wait until after views have been updated on the main thread before sending layout events. This means that any code that relies on those events to update the UI will lag the atual layout by at least one frame. This changes the RCTUIManager to send the event immediately after layout has occured on the shadow thread. This noticably improves the respinsiveness of the layout example in UIExplorer, which now updates the dimension labels immediately instead of waiting until after the layout animation has completed. --- Examples/UIExplorer/LayoutEventsExample.js | 6 ++-- Libraries/Components/View/View.js | 4 +++ React/Modules/RCTUIManager.m | 36 +++++++++------------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Examples/UIExplorer/LayoutEventsExample.js b/Examples/UIExplorer/LayoutEventsExample.js index 3407be48a..4dec90f8f 100644 --- a/Examples/UIExplorer/LayoutEventsExample.js +++ b/Examples/UIExplorer/LayoutEventsExample.js @@ -85,8 +85,7 @@ var LayoutEventExample = React.createClass({ return ( - onLayout events are called on mount and whenever layout is updated, - including after layout animations complete.{' '} + layout events are called on mount and whenever layout is recalculated. Note that the layout event will typically be received before the layout has updated on screen, especially when using layout animations.{' '} Press here to change layout. @@ -136,6 +135,9 @@ var styles = StyleSheet.create({ pressText: { fontWeight: 'bold', }, + italicText: { + fontStyle: 'italic', + }, }); exports.title = 'Layout Events'; diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index fdb2ff6ed..d5235b5be 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -185,6 +185,10 @@ var View = React.createClass({ * Invoked on mount and layout changes with * * {nativeEvent: { layout: {x, y, width, height}}}. + * + * This event is fired immediately once the layout has been calculated, but + * the new layout may not yet be reflected on the screen at the time the + * event is received, especially if a layout animation is in progress. */ onLayout: PropTypes.func, diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 4969b0055..9acaff806 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -448,29 +448,12 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); NSMutableArray *frames = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *areNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; NSMutableArray *parentsAreNew = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; - NSMutableArray *onLayoutEvents = [NSMutableArray arrayWithCapacity:viewsWithNewFrames.count]; for (RCTShadowView *shadowView in viewsWithNewFrames) { [frameReactTags addObject:shadowView.reactTag]; [frames addObject:[NSValue valueWithCGRect:shadowView.frame]]; [areNew addObject:@(shadowView.isNewView)]; [parentsAreNew addObject:@(shadowView.superview.isNewView)]; - - // TODO (#8214142): this can be greatly simplified by sending the layout - // event directly from the shadow thread, which may be better anyway. - id event = (id)kCFNull; - if (shadowView.onLayout) { - event = @{ - @"target": shadowView.reactTag, - @"layout": @{ - @"x": @(shadowView.frame.origin.x), - @"y": @(shadowView.frame.origin.y), - @"width": @(shadowView.frame.size.width), - @"height": @(shadowView.frame.size.height), - }, - }; - } - [onLayoutEvents addObject:event]; } for (RCTShadowView *shadowView in viewsWithNewFrames) { @@ -486,7 +469,20 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); for (RCTShadowView *shadowView in viewsWithNewFrames) { RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager]; RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView]; - if (block) [updateBlocks addObject:block]; + if (shadowView.onLayout) { + CGRect frame = shadowView.frame; + shadowView.onLayout(@{ + @"layout": @{ + @"x": @(frame.origin.x), + @"y": @(frame.origin.y), + @"width": @(frame.size.width), + @"height": @(frame.size.height), + }, + }); + } + if (block) { + [updateBlocks addObject:block]; + } } // Perform layout (possibly animated) @@ -497,7 +493,6 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); NSNumber *reactTag = frameReactTags[ii]; UIView *view = viewRegistry[reactTag]; CGRect frame = [frames[ii] CGRectValue]; - id event = onLayoutEvents[ii]; BOOL isNew = [areNew[ii] boolValue]; RCTAnimation *updateAnimation = isNew ? nil : _layoutAnimation.updateAnimation; @@ -506,9 +501,6 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls); void (^completion)(BOOL) = ^(BOOL finished) { completionsCalled++; - if (event != (id)kCFNull) { - [self.bridge.eventDispatcher sendInputEventWithName:@"layout" body:event]; - } if (callback && completionsCalled == frames.count - 1) { callback(@[@(finished)]); } From 8187d1f0ec867d252c8e91ca2a8475a185c2bd03 Mon Sep 17 00:00:00 2001 From: Alex Akers Date: Thu, 3 Sep 2015 05:53:16 -0700 Subject: [PATCH 26/27] Update image loader plugins --- .../UIExplorerUnitTests/RCTImageLoaderTests.m | 6 +-- Libraries/Image/RCTGIFImageDecoder.m | 2 +- Libraries/Image/RCTImageDownloader.m | 38 ++++++++++++++++-- Libraries/Image/RCTImageLoader.m | 16 +++++++- Libraries/Image/RCTImageStoreManager.h | 2 +- Libraries/Image/RCTImageStoreManager.m | 40 ------------------- 6 files changed, 52 insertions(+), 52 deletions(-) diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m index 256f0e5c5..d0323df7a 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTImageLoaderTests.m @@ -50,14 +50,13 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) RCTImageLoader *imageLoader = [RCTImageLoader new]; NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader, imageLoader]; } launchOptions:nil]; - RCTImageLoaderCancellationBlock cancelBlock = [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); } completionBlock:^(NSError *loadError, id loadedImage) { XCTAssertEqualObjects(loadedImage, image); XCTAssertNil(loadError); }]; - XCTAssertNil(cancelBlock); } - (void)testImageLoaderUsesImageURLLoaderWithHighestPriority @@ -82,14 +81,13 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2) RCTImageLoader *imageLoader = [RCTImageLoader new]; NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[loader1, loader2, imageLoader]; } launchOptions:nil]; - RCTImageLoaderCancellationBlock cancelBlock = [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { + [imageLoader loadImageWithTag:@"http://facebook.github.io/react/img/logo_og.png" size:CGSizeMake(100, 100) scale:1.0 resizeMode:UIViewContentModeScaleAspectFit progressBlock:^(int64_t progress, int64_t total) { XCTAssertEqual(progress, 1); XCTAssertEqual(total, 1); } completionBlock:^(NSError *loadError, id loadedImage) { XCTAssertEqualObjects(loadedImage, image); XCTAssertNil(loadError); }]; - XCTAssertNil(cancelBlock); } - (void)testImageDecoding diff --git a/Libraries/Image/RCTGIFImageDecoder.m b/Libraries/Image/RCTGIFImageDecoder.m index 0419ed728..3d85f94e7 100644 --- a/Libraries/Image/RCTGIFImageDecoder.m +++ b/Libraries/Image/RCTGIFImageDecoder.m @@ -24,7 +24,7 @@ RCT_EXPORT_MODULE() char header[7] = {}; [imageData getBytes:header length:6]; - return !strcmp(header, "GIF87a") == 0 || !strcmp(header, "GIF89a"); + return !strcmp(header, "GIF87a") || !strcmp(header, "GIF89a"); } - (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index 82f777895..f110bd628 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -37,8 +37,10 @@ RCT_EXPORT_MODULE() - (BOOL)canLoadImageURL:(NSURL *)requestURL { // Have to exclude 'file://' from the main bundle, otherwise this would conflict with RCTAssetBundleImageLoader - return [requestURL.scheme.lowercaseString hasPrefix:@"http"] || - ([requestURL.scheme.lowercaseString hasPrefix:@"file"] && ![requestURL.path hasPrefix:[NSBundle mainBundle].resourcePath]); + return + [requestURL.scheme compare:@"http" options:NSCaseInsensitiveSearch range:NSMakeRange(0, 4)] == NSOrderedSame || + ([requestURL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame && ![requestURL.path hasPrefix:[NSBundle mainBundle].resourcePath]) || + [requestURL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame; } /** @@ -103,7 +105,7 @@ RCT_EXPORT_MODULE() progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler { - if ([imageURL.scheme isEqualToString:@"http"]) { + if ([imageURL.scheme.lowercaseString hasPrefix:@"http"]) { __block RCTImageLoaderCancellationBlock decodeCancel = nil; __weak RCTImageDownloader *weakSelf = self; @@ -122,9 +124,37 @@ RCT_EXPORT_MODULE() decodeCancel(); } }; + } else if ([imageURL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame) { + __block BOOL cancelled = NO; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + if (cancelled) { + return; + } + + // Normally -dataWithContentsOfURL: would be bad but this is a data URL. + NSData *data = [NSData dataWithContentsOfURL:imageURL]; + + UIImage *image = [UIImage imageWithData:data]; + if (image) { + if (progressHandler) { + progressHandler(1, 1); + } + if (completionHandler) { + completionHandler(nil, image); + } + } else { + if (completionHandler) { + NSString *message = [NSString stringWithFormat:@"Invalid image data for URL: %@", imageURL]; + completionHandler(RCTErrorWithMessage(message), nil); + } + } + }); + return ^{ + cancelled = YES; + }; } else if ([imageURL.scheme isEqualToString:@"file"]) { __block BOOL cancelled = NO; - dispatch_async(dispatch_get_main_queue(), ^{ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ if (cancelled) { return; } diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index ba4ac7ea1..422a189d5 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -87,9 +87,21 @@ RCT_EXPORT_MODULE() RCTLogError(@"No suitable image URL loader found for %@", imageTag); } - return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:progressBlock completionHandler:^(NSError *error, id image) { + return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:^(int64_t progress, int64_t total) { + if (!progressBlock) { + return; + } + + if ([NSThread isMainThread]) { + progressBlock(progress, total); + } else { + dispatch_async(dispatch_get_main_queue(), ^{ + progressBlock(progress, total); + }); + } + } completionHandler:^(NSError *error, id image) { RCTDispatchCallbackOnMainQueue(completionBlock, error, image); - }]; + }] ?: ^{}; } - (id)imageDecoderForRequest:(NSData *)imageData diff --git a/Libraries/Image/RCTImageStoreManager.h b/Libraries/Image/RCTImageStoreManager.h index 5f6a1cd64..9862d1bb2 100644 --- a/Libraries/Image/RCTImageStoreManager.h +++ b/Libraries/Image/RCTImageStoreManager.h @@ -6,7 +6,7 @@ #import "RCTImageLoader.h" #import "RCTURLRequestHandler.h" -@interface RCTImageStoreManager : NSObject +@interface RCTImageStoreManager : NSObject /** * Set and get cached images. These must be called from the main thread. diff --git a/Libraries/Image/RCTImageStoreManager.m b/Libraries/Image/RCTImageStoreManager.m index c7f5a9fd8..65a2e63f2 100644 --- a/Libraries/Image/RCTImageStoreManager.m +++ b/Libraries/Image/RCTImageStoreManager.m @@ -97,46 +97,6 @@ RCT_EXPORT_METHOD(addImageFromBase64:(NSString *)base64String } } -#pragma mark - RCTURLRequestHandler - -- (BOOL)canHandleRequest:(NSURLRequest *)request -{ - return [request.URL.scheme.lowercaseString isEqualToString:@"rct-image-store"]; -} - -- (id)sendRequest:(NSURLRequest *)request - withDelegate:(id)delegate -{ - NSString *imageTag = request.URL.absoluteString; - [self getImageForTag:imageTag withBlock:^(UIImage *image) { - if (!image) { - NSError *error = RCTErrorWithMessage([NSString stringWithFormat:@"Invalid imageTag: %@", imageTag]); - [delegate URLRequest:request didCompleteWithError:error]; - return; - } - - NSString *mimeType = nil; - NSData *imageData = nil; - if (RCTImageHasAlpha(image.CGImage)) { - mimeType = @"image/png"; - imageData = UIImagePNGRepresentation(image); - } else { - mimeType = @"image/jpeg"; - imageData = UIImageJPEGRepresentation(image, 1.0); - } - - NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL - MIMEType:mimeType - expectedContentLength:imageData.length - textEncodingName:nil]; - - [delegate URLRequest:request didReceiveResponse:response]; - [delegate URLRequest:request didReceiveData:imageData]; - [delegate URLRequest:request didCompleteWithError:nil]; - }]; - return request; -} - #pragma mark - RCTImageLoader - (BOOL)canLoadImageURL:(NSURL *)requestURL From 200fa6debc90d4dee1b9157b85b8c58fd15dd35d Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 3 Sep 2015 11:45:36 -0100 Subject: [PATCH 27/27] [ReactNative] Bump node_modules and add npm-shrinkwrap --- npm-shrinkwrap.json | 5759 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 5759 insertions(+) create mode 100644 npm-shrinkwrap.json diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json new file mode 100644 index 000000000..9e5c55c36 --- /dev/null +++ b/npm-shrinkwrap.json @@ -0,0 +1,5759 @@ +{ + "name": "react-native", + "version": "0.8.0", + "dependencies": { + "absolute-path": { + "version": "0.0.0", + "from": "absolute-path@0.0.0", + "resolved": "https://registry.npmjs.org/absolute-path/-/absolute-path-0.0.0.tgz" + }, + "babel": { + "version": "5.8.21", + "from": "babel@5.8.21", + "resolved": "https://registry.npmjs.org/babel/-/babel-5.8.21.tgz", + "dependencies": { + "chokidar": { + "version": "1.0.5", + "from": "chokidar@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.0.5.tgz", + "dependencies": { + "anymatch": { + "version": "1.3.0", + "from": "anymatch@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", + "dependencies": { + "micromatch": { + "version": "2.2.0", + "from": "micromatch@>=2.1.5 <3.0.0", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.2.0.tgz", + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "from": "arr-diff@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "dependencies": { + "arr-flatten": { + "version": "1.0.1", + "from": "arr-flatten@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.0.1.tgz" + }, + "array-slice": { + "version": "0.2.3", + "from": "array-slice@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz" + } + } + }, + "array-unique": { + "version": "0.2.1", + "from": "array-unique@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz" + }, + "braces": { + "version": "1.8.1", + "from": "braces@>=1.8.0 <2.0.0", + "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.1.tgz", + "dependencies": { + "expand-range": { + "version": "1.8.1", + "from": "expand-range@>=1.8.1 <2.0.0", + "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.1.tgz", + "dependencies": { + "fill-range": { + "version": "2.2.2", + "from": "fill-range@>=2.1.0 <3.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.2.tgz", + "dependencies": { + "is-number": { + "version": "1.1.2", + "from": "is-number@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-1.1.2.tgz" + }, + "isobject": { + "version": "1.0.2", + "from": "isobject@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-1.0.2.tgz" + }, + "randomatic": { + "version": "1.1.0", + "from": "randomatic@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.0.tgz" + }, + "repeat-string": { + "version": "1.5.2", + "from": "repeat-string@>=1.5.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.5.2.tgz" + } + } + } + } + }, + "lazy-cache": { + "version": "0.2.3", + "from": "lazy-cache@>=0.2.3 <0.3.0", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.3.tgz" + }, + "preserve": { + "version": "0.2.0", + "from": "preserve@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz" + }, + "repeat-element": { + "version": "1.1.2", + "from": "repeat-element@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz" + } + } + }, + "expand-brackets": { + "version": "0.1.4", + "from": "expand-brackets@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.4.tgz" + }, + "extglob": { + "version": "0.3.1", + "from": "extglob@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.1.tgz", + "dependencies": { + "ansi-green": { + "version": "0.1.1", + "from": "ansi-green@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ansi-green/-/ansi-green-0.1.1.tgz", + "dependencies": { + "ansi-wrap": { + "version": "0.1.0", + "from": "ansi-wrap@0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz" + } + } + }, + "is-extglob": { + "version": "1.0.0", + "from": "is-extglob@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + }, + "success-symbol": { + "version": "0.1.0", + "from": "success-symbol@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/success-symbol/-/success-symbol-0.1.0.tgz" + } + } + }, + "filename-regex": { + "version": "2.0.0", + "from": "filename-regex@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.0.tgz" + }, + "kind-of": { + "version": "1.1.0", + "from": "kind-of@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz" + }, + "object.omit": { + "version": "1.1.0", + "from": "object.omit@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-1.1.0.tgz", + "dependencies": { + "for-own": { + "version": "0.1.3", + "from": "for-own@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.3.tgz", + "dependencies": { + "for-in": { + "version": "0.1.4", + "from": "for-in@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.4.tgz" + } + } + }, + "isobject": { + "version": "1.0.2", + "from": "isobject@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-1.0.2.tgz" + } + } + }, + "parse-glob": { + "version": "3.0.2", + "from": "parse-glob@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.2.tgz", + "dependencies": { + "glob-base": { + "version": "0.2.0", + "from": "glob-base@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.2.0.tgz" + }, + "is-dotfile": { + "version": "1.0.1", + "from": "is-dotfile@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.1.tgz" + }, + "is-extglob": { + "version": "1.0.0", + "from": "is-extglob@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz" + } + } + }, + "regex-cache": { + "version": "0.4.2", + "from": "regex-cache@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.2.tgz", + "dependencies": { + "is-equal-shallow": { + "version": "0.1.3", + "from": "is-equal-shallow@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz" + }, + "is-primitive": { + "version": "2.0.0", + "from": "is-primitive@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz" + } + } + } + } + } + } + }, + "arrify": { + "version": "1.0.0", + "from": "arrify@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.0.tgz" + }, + "async-each": { + "version": "0.1.6", + "from": "async-each@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-0.1.6.tgz" + }, + "glob-parent": { + "version": "1.2.0", + "from": "glob-parent@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-1.2.0.tgz" + }, + "is-binary-path": { + "version": "1.0.1", + "from": "is-binary-path@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "dependencies": { + "binary-extensions": { + "version": "1.3.1", + "from": "binary-extensions@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.3.1.tgz" + } + } + }, + "is-glob": { + "version": "1.1.3", + "from": "is-glob@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-1.1.3.tgz" + }, + "readdirp": { + "version": "1.4.0", + "from": "readdirp@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-1.4.0.tgz", + "dependencies": { + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.12 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.26-2 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "fsevents": { + "version": "0.3.8", + "from": "fsevents@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.3.8.tgz", + "dependencies": { + "nan": { + "version": "2.0.8", + "from": "nan@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.8.tgz" + } + } + } + } + }, + "commander": { + "version": "2.8.1", + "from": "commander@>=2.6.0 <3.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + } + }, + "convert-source-map": { + "version": "1.1.1", + "from": "convert-source-map@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.1.tgz" + }, + "fs-readdir-recursive": { + "version": "0.1.2", + "from": "fs-readdir-recursive@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz" + }, + "glob": { + "version": "5.0.14", + "from": "glob@>=5.0.5 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.14.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "output-file-sync": { + "version": "1.1.1", + "from": "output-file-sync@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "path-exists": { + "version": "1.0.0", + "from": "path-exists@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "slash": { + "version": "1.0.0", + "from": "slash@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" + } + } + }, + "babel-core": { + "version": "5.8.21", + "from": "babel-core@5.8.21", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-5.8.21.tgz", + "dependencies": { + "babel-plugin-constant-folding": { + "version": "1.0.1", + "from": "babel-plugin-constant-folding@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz" + }, + "babel-plugin-dead-code-elimination": { + "version": "1.0.2", + "from": "babel-plugin-dead-code-elimination@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz" + }, + "babel-plugin-eval": { + "version": "1.0.1", + "from": "babel-plugin-eval@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz" + }, + "babel-plugin-inline-environment-variables": { + "version": "1.0.1", + "from": "babel-plugin-inline-environment-variables@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz" + }, + "babel-plugin-jscript": { + "version": "1.0.4", + "from": "babel-plugin-jscript@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz" + }, + "babel-plugin-member-expression-literals": { + "version": "1.0.1", + "from": "babel-plugin-member-expression-literals@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz" + }, + "babel-plugin-property-literals": { + "version": "1.0.1", + "from": "babel-plugin-property-literals@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz" + }, + "babel-plugin-proto-to-assign": { + "version": "1.0.4", + "from": "babel-plugin-proto-to-assign@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz" + }, + "babel-plugin-react-constant-elements": { + "version": "1.0.3", + "from": "babel-plugin-react-constant-elements@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz" + }, + "babel-plugin-react-display-name": { + "version": "1.0.3", + "from": "babel-plugin-react-display-name@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz" + }, + "babel-plugin-remove-console": { + "version": "1.0.1", + "from": "babel-plugin-remove-console@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz" + }, + "babel-plugin-remove-debugger": { + "version": "1.0.1", + "from": "babel-plugin-remove-debugger@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz" + }, + "babel-plugin-runtime": { + "version": "1.0.7", + "from": "babel-plugin-runtime@>=1.0.7 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz" + }, + "babel-plugin-undeclared-variables-check": { + "version": "1.0.2", + "from": "babel-plugin-undeclared-variables-check@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz", + "dependencies": { + "leven": { + "version": "1.0.2", + "from": "leven@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-1.0.2.tgz" + } + } + }, + "babel-plugin-undefined-to-void": { + "version": "1.1.6", + "from": "babel-plugin-undefined-to-void@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz" + }, + "babylon": { + "version": "5.8.23", + "from": "babylon@>=5.8.21 <6.0.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-5.8.23.tgz" + }, + "bluebird": { + "version": "2.9.34", + "from": "bluebird@>=2.9.33 <3.0.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz" + }, + "convert-source-map": { + "version": "1.1.1", + "from": "convert-source-map@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.1.tgz" + }, + "core-js": { + "version": "1.1.3", + "from": "core-js@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.3.tgz" + }, + "debug": { + "version": "2.2.0", + "from": "debug@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "dependencies": { + "ms": { + "version": "0.7.1", + "from": "ms@0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" + } + } + }, + "detect-indent": { + "version": "3.0.1", + "from": "detect-indent@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-3.0.1.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + } + } + }, + "esutils": { + "version": "2.0.2", + "from": "esutils@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz" + }, + "fs-readdir-recursive": { + "version": "0.1.2", + "from": "fs-readdir-recursive@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz" + }, + "globals": { + "version": "6.4.1", + "from": "globals@>=6.4.0 <7.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-6.4.1.tgz" + }, + "home-or-tmp": { + "version": "1.0.0", + "from": "home-or-tmp@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-1.0.0.tgz", + "dependencies": { + "os-tmpdir": { + "version": "1.0.1", + "from": "os-tmpdir@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.1.tgz" + }, + "user-home": { + "version": "1.1.1", + "from": "user-home@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-1.1.1.tgz" + } + } + }, + "is-integer": { + "version": "1.0.6", + "from": "is-integer@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.6.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + }, + "js-tokens": { + "version": "1.0.1", + "from": "js-tokens@1.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-1.0.1.tgz" + }, + "json5": { + "version": "0.4.0", + "from": "json5@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz" + }, + "line-numbers": { + "version": "0.2.0", + "from": "line-numbers@0.2.0", + "resolved": "https://registry.npmjs.org/line-numbers/-/line-numbers-0.2.0.tgz", + "dependencies": { + "left-pad": { + "version": "0.0.3", + "from": "left-pad@0.0.3", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-0.0.3.tgz" + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.10.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.3 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "output-file-sync": { + "version": "1.1.1", + "from": "output-file-sync@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-1.1.1.tgz", + "dependencies": { + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "path-exists": { + "version": "1.0.0", + "from": "path-exists@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "regenerator": { + "version": "0.8.35", + "from": "regenerator@0.8.35", + "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.35.tgz", + "dependencies": { + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + }, + "defs": { + "version": "1.1.0", + "from": "defs@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.0.tgz", + "dependencies": { + "alter": { + "version": "0.2.0", + "from": "alter@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "dependencies": { + "stable": { + "version": "0.1.5", + "from": "stable@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" + } + } + }, + "ast-traverse": { + "version": "0.1.1", + "from": "ast-traverse@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz" + }, + "breakable": { + "version": "1.0.0", + "from": "breakable@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz" + }, + "esprima-fb": { + "version": "8001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=8001.1001.0-dev-harmony-fb <8001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-8001.1001.0-dev-harmony-fb.tgz" + }, + "simple-fmt": { + "version": "0.1.0", + "from": "simple-fmt@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" + }, + "simple-is": { + "version": "0.2.0", + "from": "simple-is@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" + }, + "stringmap": { + "version": "0.2.2", + "from": "stringmap@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" + }, + "stringset": { + "version": "0.2.1", + "from": "stringset@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" + }, + "tryor": { + "version": "0.1.2", + "from": "tryor@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" + } + } + }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1.0-dev-harmony-fb <15001.2.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz" + }, + "recast": { + "version": "0.10.24", + "from": "recast@0.10.24", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.24.tgz", + "dependencies": { + "ast-types": { + "version": "0.8.5", + "from": "ast-types@0.8.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.5.tgz" + } + } + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <2.4.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "regexpu": { + "version": "1.2.0", + "from": "regexpu@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/regexpu/-/regexpu-1.2.0.tgz", + "dependencies": { + "recast": { + "version": "0.10.32", + "from": "recast@>=0.10.6 <0.11.0", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.32.tgz", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz" + }, + "ast-types": { + "version": "0.8.11", + "from": "ast-types@0.8.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.11.tgz" + } + } + }, + "regenerate": { + "version": "1.2.1", + "from": "regenerate@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.2.1.tgz" + }, + "regjsgen": { + "version": "0.2.0", + "from": "regjsgen@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz" + }, + "regjsparser": { + "version": "0.1.5", + "from": "regjsparser@>=0.1.4 <0.2.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "dependencies": { + "jsesc": { + "version": "0.5.0", + "from": "jsesc@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + } + } + } + } + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + }, + "resolve": { + "version": "1.1.6", + "from": "resolve@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz" + }, + "shebang-regex": { + "version": "1.0.0", + "from": "shebang-regex@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + }, + "slash": { + "version": "1.0.0", + "from": "slash@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "source-map-support": { + "version": "0.2.10", + "from": "source-map-support@>=0.2.10 <0.3.0", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.2.10.tgz", + "dependencies": { + "source-map": { + "version": "0.1.32", + "from": "source-map@0.1.32", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.32.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + } + } + }, + "to-fast-properties": { + "version": "1.0.1", + "from": "to-fast-properties@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.1.tgz" + }, + "trim-right": { + "version": "1.0.1", + "from": "trim-right@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz" + }, + "try-resolve": { + "version": "1.0.1", + "from": "try-resolve@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/try-resolve/-/try-resolve-1.0.1.tgz" + } + } + }, + "bser": { + "version": "1.0.0", + "from": "bser@1.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.0.tgz", + "dependencies": { + "node-int64": { + "version": "0.4.0", + "from": "node-int64@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + } + } + }, + "chalk": { + "version": "1.0.0", + "from": "chalk@1.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.0.0.tgz", + "dependencies": { + "ansi-styles": { + "version": "2.1.0", + "from": "ansi-styles@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.1.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + }, + "has-ansi": { + "version": "1.0.3", + "from": "has-ansi@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-1.0.3.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + }, + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + } + } + }, + "strip-ansi": { + "version": "2.0.1", + "from": "strip-ansi@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + } + } + }, + "supports-color": { + "version": "1.3.1", + "from": "supports-color@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.3.1.tgz" + } + } + }, + "connect": { + "version": "2.8.3", + "from": "connect@2.8.3", + "resolved": "https://registry.npmjs.org/connect/-/connect-2.8.3.tgz", + "dependencies": { + "qs": { + "version": "0.6.5", + "from": "qs@0.6.5", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.5.tgz" + }, + "formidable": { + "version": "1.0.14", + "from": "formidable@1.0.14", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.14.tgz" + }, + "cookie-signature": { + "version": "1.0.1", + "from": "cookie-signature@1.0.1", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.1.tgz" + }, + "buffer-crc32": { + "version": "0.2.1", + "from": "buffer-crc32@0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz" + }, + "cookie": { + "version": "0.1.0", + "from": "cookie@0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz" + }, + "send": { + "version": "0.1.2", + "from": "send@0.1.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.2.tgz", + "dependencies": { + "mime": { + "version": "1.2.11", + "from": "mime@>=1.2.9 <1.3.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz" + }, + "range-parser": { + "version": "0.0.4", + "from": "range-parser@0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz" + } + } + }, + "bytes": { + "version": "0.2.0", + "from": "bytes@0.2.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.2.0.tgz" + }, + "fresh": { + "version": "0.1.0", + "from": "fresh@0.1.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.1.0.tgz" + }, + "pause": { + "version": "0.0.1", + "from": "pause@0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz" + }, + "uid2": { + "version": "0.0.2", + "from": "uid2@0.0.2", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.2.tgz" + }, + "methods": { + "version": "0.0.1", + "from": "methods@0.0.1", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz" + } + } + }, + "debug": { + "version": "2.1.0", + "from": "debug@2.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz", + "dependencies": { + "ms": { + "version": "0.6.2", + "from": "ms@0.6.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" + } + } + }, + "graceful-fs": { + "version": "4.1.2", + "from": "graceful-fs@4.1.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.2.tgz" + }, + "image-size": { + "version": "0.3.5", + "from": "image-size@0.3.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.3.5.tgz" + }, + "immutable": { + "version": "3.7.5", + "from": "immutable@>=3.7.4 <4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.5.tgz" + }, + "joi": { + "version": "5.1.0", + "from": "joi@5.1.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-5.1.0.tgz", + "dependencies": { + "hoek": { + "version": "2.14.0", + "from": "hoek@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.14.0.tgz" + }, + "topo": { + "version": "1.0.3", + "from": "topo@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-1.0.3.tgz" + }, + "isemail": { + "version": "1.1.1", + "from": "isemail@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.1.1.tgz" + }, + "moment": { + "version": "2.10.6", + "from": "moment@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.10.6.tgz" + } + } + }, + "jstransform": { + "version": "11.0.1", + "from": "jstransform@11.0.1", + "resolved": "https://registry.npmjs.org/jstransform/-/jstransform-11.0.1.tgz", + "dependencies": { + "base62": { + "version": "1.1.0", + "from": "base62@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/base62/-/base62-1.1.0.tgz" + }, + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.1 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "recast": { + "version": "0.10.32", + "from": "recast@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.32.tgz", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz" + }, + "ast-types": { + "version": "0.8.11", + "from": "ast-types@0.8.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.11.tgz" + } + } + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1.0-dev-harmony-fb <15002.0.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz" + }, + "object-assign": { + "version": "2.1.1", + "from": "object-assign@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.2 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + } + } + }, + "module-deps": { + "version": "3.5.6", + "from": "module-deps@3.5.6", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-3.5.6.tgz", + "dependencies": { + "JSONStream": { + "version": "0.7.4", + "from": "JSONStream@>=0.7.1 <0.8.0", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.7.4.tgz", + "dependencies": { + "jsonparse": { + "version": "0.0.5", + "from": "jsonparse@0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.2.7 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "browser-resolve": { + "version": "1.9.1", + "from": "browser-resolve@>=1.3.1 <2.0.0", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.9.1.tgz", + "dependencies": { + "resolve": { + "version": "1.1.6", + "from": "resolve@1.1.6", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.6.tgz" + } + } + }, + "concat-stream": { + "version": "1.4.10", + "from": "concat-stream@>=1.4.5 <1.5.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.4.10.tgz", + "dependencies": { + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + } + } + }, + "detective": { + "version": "3.1.0", + "from": "detective@>=3.1.0 <4.0.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-3.1.0.tgz", + "dependencies": { + "escodegen": { + "version": "1.1.0", + "from": "escodegen@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.1.0.tgz", + "dependencies": { + "esprima": { + "version": "1.0.4", + "from": "esprima@>=1.0.4 <1.1.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz" + }, + "estraverse": { + "version": "1.5.1", + "from": "estraverse@>=1.5.0 <1.6.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz" + }, + "esutils": { + "version": "1.0.0", + "from": "esutils@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz" + } + } + }, + "esprima-fb": { + "version": "3001.1.0-dev-harmony-fb", + "from": "esprima-fb@3001.1.0-dev-harmony-fb", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-3001.0001.0000-dev-harmony-fb.tgz" + } + } + }, + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimist": { + "version": "0.2.0", + "from": "minimist@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.2.0.tgz" + }, + "parents": { + "version": "1.0.1", + "from": "parents@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "dependencies": { + "path-platform": { + "version": "0.11.15", + "from": "path-platform@>=0.11.15 <0.12.0", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz" + } + } + }, + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.0.27-1 <2.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + }, + "resolve": { + "version": "0.7.4", + "from": "resolve@>=0.7.2 <0.8.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.7.4.tgz" + }, + "shallow-copy": { + "version": "0.0.1", + "from": "shallow-copy@0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz" + }, + "stream-combiner2": { + "version": "1.0.2", + "from": "stream-combiner2@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.0.2.tgz", + "dependencies": { + "through2": { + "version": "0.5.1", + "from": "through2@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + }, + "xtend": { + "version": "3.0.0", + "from": "xtend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz" + } + } + } + } + }, + "subarg": { + "version": "0.0.1", + "from": "subarg@0.0.1", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-0.0.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.7 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + }, + "through2": { + "version": "0.4.2", + "from": "through2@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + } + } + }, + "xtend": { + "version": "2.1.2", + "from": "xtend@>=2.1.1 <2.2.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "dependencies": { + "object-keys": { + "version": "0.4.0", + "from": "object-keys@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz" + } + } + } + } + } + } + }, + "optimist": { + "version": "0.6.1", + "from": "optimist@0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + }, + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.1 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + } + } + }, + "progress": { + "version": "1.1.8", + "from": "progress@>=1.1.8 <2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz" + }, + "promise": { + "version": "7.0.4", + "from": "promise@>=7.0.3 <8.0.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.0.4.tgz", + "dependencies": { + "asap": { + "version": "2.0.3", + "from": "asap@>=2.0.3 <2.1.0", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.3.tgz" + } + } + }, + "react-timer-mixin": { + "version": "0.13.2", + "from": "react-timer-mixin@>=0.13.1 <0.14.0", + "resolved": "https://registry.npmjs.org/react-timer-mixin/-/react-timer-mixin-0.13.2.tgz" + }, + "react-tools": { + "version": "0.14.0-beta1", + "from": "git://github.com/facebook/react.git#b4e74e38e43ac53af8acd62c78c9213be0194245", + "resolved": "git://github.com/facebook/react.git#b4e74e38e43ac53af8acd62c78c9213be0194245", + "dependencies": { + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "recast": { + "version": "0.10.32", + "from": "recast@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.32.tgz", + "dependencies": { + "esprima-fb": { + "version": "15001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1001.0-dev-harmony-fb <15001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz" + }, + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.4 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "ast-types": { + "version": "0.8.11", + "from": "ast-types@0.8.11", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.11.tgz" + } + } + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.6 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + } + } + }, + "rebound": { + "version": "0.0.12", + "from": "rebound@>=0.0.12 <0.0.13", + "resolved": "https://registry.npmjs.org/rebound/-/rebound-0.0.12.tgz" + }, + "regenerator": { + "version": "0.8.36", + "from": "regenerator@0.8.36", + "resolved": "https://registry.npmjs.org/regenerator/-/regenerator-0.8.36.tgz", + "dependencies": { + "commoner": { + "version": "0.10.3", + "from": "commoner@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/commoner/-/commoner-0.10.3.tgz", + "dependencies": { + "q": { + "version": "1.1.2", + "from": "q@>=1.1.2 <1.2.0", + "resolved": "https://registry.npmjs.org/q/-/q-1.1.2.tgz" + }, + "commander": { + "version": "2.5.1", + "from": "commander@>=2.5.0 <2.6.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.5.1.tgz" + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.4 <3.1.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "glob": { + "version": "4.2.2", + "from": "glob@>=4.2.1 <4.3.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.2.2.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "1.0.0", + "from": "minimatch@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "install": { + "version": "0.1.8", + "from": "install@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.1.8.tgz" + }, + "iconv-lite": { + "version": "0.4.11", + "from": "iconv-lite@>=0.4.5 <0.5.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" + } + } + }, + "defs": { + "version": "1.1.0", + "from": "defs@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/defs/-/defs-1.1.0.tgz", + "dependencies": { + "alter": { + "version": "0.2.0", + "from": "alter@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/alter/-/alter-0.2.0.tgz", + "dependencies": { + "stable": { + "version": "0.1.5", + "from": "stable@>=0.1.3 <0.2.0", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.5.tgz" + } + } + }, + "ast-traverse": { + "version": "0.1.1", + "from": "ast-traverse@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/ast-traverse/-/ast-traverse-0.1.1.tgz" + }, + "breakable": { + "version": "1.0.0", + "from": "breakable@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/breakable/-/breakable-1.0.0.tgz" + }, + "esprima-fb": { + "version": "8001.1001.0-dev-harmony-fb", + "from": "esprima-fb@>=8001.1001.0-dev-harmony-fb <8001.1002.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-8001.1001.0-dev-harmony-fb.tgz" + }, + "simple-fmt": { + "version": "0.1.0", + "from": "simple-fmt@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/simple-fmt/-/simple-fmt-0.1.0.tgz" + }, + "simple-is": { + "version": "0.2.0", + "from": "simple-is@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz" + }, + "stringmap": { + "version": "0.2.2", + "from": "stringmap@>=0.2.2 <0.3.0", + "resolved": "https://registry.npmjs.org/stringmap/-/stringmap-0.2.2.tgz" + }, + "stringset": { + "version": "0.2.1", + "from": "stringset@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/stringset/-/stringset-0.2.1.tgz" + }, + "tryor": { + "version": "0.1.2", + "from": "tryor@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/tryor/-/tryor-0.1.2.tgz" + } + } + }, + "esprima-fb": { + "version": "15001.1.0-dev-harmony-fb", + "from": "esprima-fb@>=15001.1.0-dev-harmony-fb <15001.2.0", + "resolved": "https://registry.npmjs.org/esprima-fb/-/esprima-fb-15001.1.0-dev-harmony-fb.tgz" + }, + "private": { + "version": "0.1.6", + "from": "private@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.6.tgz" + }, + "recast": { + "version": "0.10.25", + "from": "recast@0.10.25", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.10.25.tgz", + "dependencies": { + "source-map": { + "version": "0.4.4", + "from": "source-map@>=0.4.1 <0.5.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "ast-types": { + "version": "0.8.5", + "from": "ast-types@0.8.5", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.8.5.tgz" + } + } + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <2.4.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "sane": { + "version": "1.2.0", + "from": "sane@>=1.1.2 <2.0.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-1.2.0.tgz", + "dependencies": { + "exec-sh": { + "version": "0.2.0", + "from": "exec-sh@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", + "dependencies": { + "merge": { + "version": "1.2.0", + "from": "merge@>=1.1.3 <2.0.0", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz" + } + } + }, + "fb-watchman": { + "version": "1.6.0", + "from": "fb-watchman@>=1.5.0 <2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-1.6.0.tgz", + "dependencies": { + "bser": { + "version": "1.0.2", + "from": "bser@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-1.0.2.tgz", + "dependencies": { + "node-int64": { + "version": "0.4.0", + "from": "node-int64@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + } + } + } + } + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.14 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "walker": { + "version": "1.0.7", + "from": "walker@>=1.0.5 <1.1.0", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "dependencies": { + "makeerror": { + "version": "1.0.11", + "from": "makeerror@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "dependencies": { + "tmpl": { + "version": "1.0.4", + "from": "tmpl@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz" + } + } + } + } + }, + "watch": { + "version": "0.10.0", + "from": "watch@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/watch/-/watch-0.10.0.tgz" + } + } + }, + "semver": { + "version": "4.3.6", + "from": "semver@>=4.3.6 <5.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz" + }, + "source-map": { + "version": "0.1.31", + "from": "source-map@0.1.31", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.31.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "stacktrace-parser": { + "version": "0.1.3", + "from": "stacktrace-parser@0.1.3", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.3.tgz" + }, + "uglify-js": { + "version": "2.4.16", + "from": "uglify-js@2.4.16", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.16.tgz", + "dependencies": { + "async": { + "version": "0.2.10", + "from": "async@>=0.2.6 <0.3.0", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz" + }, + "source-map": { + "version": "0.1.34", + "from": "source-map@0.1.34", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + }, + "optimist": { + "version": "0.3.7", + "from": "optimist@>=0.3.5 <0.4.0", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + } + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "from": "uglify-to-browserify@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz" + } + } + }, + "underscore": { + "version": "1.7.0", + "from": "underscore@1.7.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz" + }, + "wordwrap": { + "version": "1.0.0", + "from": "wordwrap@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz" + }, + "worker-farm": { + "version": "1.3.1", + "from": "worker-farm@>=1.3.1 <2.0.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.3.1.tgz", + "dependencies": { + "errno": { + "version": "0.1.4", + "from": "errno@>=0.1.1 <0.2.0-0", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", + "dependencies": { + "prr": { + "version": "0.0.0", + "from": "prr@>=0.0.0 <0.1.0", + "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "ws": { + "version": "0.8.0", + "from": "ws@0.8.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-0.8.0.tgz", + "dependencies": { + "options": { + "version": "0.0.6", + "from": "options@>=0.0.5", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" + }, + "ultron": { + "version": "1.0.2", + "from": "ultron@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz" + }, + "bufferutil": { + "version": "1.2.1", + "from": "bufferutil@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.2.1.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "2.0.8", + "from": "nan@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.8.tgz" + } + } + }, + "utf-8-validate": { + "version": "1.2.1", + "from": "utf-8-validate@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.2.1.tgz", + "dependencies": { + "bindings": { + "version": "1.2.1", + "from": "bindings@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz" + }, + "nan": { + "version": "2.0.8", + "from": "nan@>=2.0.5 <3.0.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.0.8.tgz" + } + } + } + } + }, + "yargs": { + "version": "1.3.2", + "from": "yargs@1.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.2.tgz" + }, + "yeoman-environment": { + "version": "1.2.7", + "from": "yeoman-environment@>=1.2.7 <2.0.0", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-1.2.7.tgz", + "dependencies": { + "diff": { + "version": "1.4.0", + "from": "diff@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz" + }, + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + }, + "globby": { + "version": "2.1.0", + "from": "globby@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-2.1.0.tgz", + "dependencies": { + "array-union": { + "version": "1.0.1", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.1.tgz", + "dependencies": { + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + } + } + }, + "async": { + "version": "1.4.2", + "from": "async@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.4.2.tgz" + }, + "glob": { + "version": "5.0.14", + "from": "glob@>=5.0.3 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.14.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "grouped-queue": { + "version": "0.3.0", + "from": "grouped-queue@>=0.3.0 <0.4.0", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-0.3.0.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + }, + "setimmediate": { + "version": "1.0.2", + "from": "setimmediate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz" + } + } + }, + "inquirer": { + "version": "0.8.5", + "from": "inquirer@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + }, + "cli-width": { + "version": "1.0.1", + "from": "cli-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.0.1.tgz" + }, + "figures": { + "version": "1.3.5", + "from": "figures@>=1.3.5 <2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.3.5.tgz" + }, + "readline2": { + "version": "0.1.1", + "from": "readline2@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", + "dependencies": { + "mute-stream": { + "version": "0.0.4", + "from": "mute-stream@0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" + }, + "strip-ansi": { + "version": "2.0.1", + "from": "strip-ansi@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz" + } + } + }, + "rx": { + "version": "2.5.3", + "from": "rx@>=2.4.3 <3.0.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "log-symbols": { + "version": "1.0.2", + "from": "log-symbols@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz" + }, + "mem-fs": { + "version": "1.1.0", + "from": "mem-fs@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.1.0.tgz", + "dependencies": { + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.3 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "vinyl": { + "version": "0.4.6", + "from": "vinyl@>=0.4.3 <0.5.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "dependencies": { + "clone": { + "version": "0.2.0", + "from": "clone@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + }, + "vinyl-file": { + "version": "1.2.1", + "from": "vinyl-file@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-1.2.1.tgz", + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "from": "strip-bom@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "dependencies": { + "is-utf8": { + "version": "0.2.0", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" + } + } + }, + "strip-bom-stream": { + "version": "1.0.0", + "from": "strip-bom-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz", + "dependencies": { + "first-chunk-stream": { + "version": "1.0.0", + "from": "first-chunk-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" + } + } + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + } + } + } + } + }, + "text-table": { + "version": "0.2.0", + "from": "text-table@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + }, + "untildify": { + "version": "2.1.0", + "from": "untildify@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-2.1.0.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.1", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" + } + } + } + } + }, + "yeoman-generator": { + "version": "0.20.3", + "from": "yeoman-generator@>=0.20.2 <0.21.0", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-0.20.3.tgz", + "dependencies": { + "async": { + "version": "1.4.2", + "from": "async@>=1.4.2 <2.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.4.2.tgz" + }, + "class-extend": { + "version": "0.1.1", + "from": "class-extend@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/class-extend/-/class-extend-0.1.1.tgz", + "dependencies": { + "lodash": { + "version": "2.4.2", + "from": "lodash@>=2.4.1 <2.5.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz" + } + } + }, + "cli-table": { + "version": "0.3.1", + "from": "cli-table@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "dependencies": { + "colors": { + "version": "1.0.3", + "from": "colors@1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" + } + } + }, + "cross-spawn": { + "version": "2.0.0", + "from": "cross-spawn@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-2.0.0.tgz", + "dependencies": { + "cross-spawn-async": { + "version": "2.0.0", + "from": "cross-spawn-async@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.0.0.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.6.5 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "which": { + "version": "1.1.1", + "from": "which@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + } + } + } + } + }, + "spawn-sync": { + "version": "1.0.13", + "from": "spawn-sync@>=1.0.13 <2.0.0", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.13.tgz", + "dependencies": { + "concat-stream": { + "version": "1.5.0", + "from": "concat-stream@>=1.4.7 <2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "os-shim": { + "version": "0.1.3", + "from": "os-shim@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz" + } + } + } + } + }, + "dargs": { + "version": "4.0.1", + "from": "dargs@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + }, + "dateformat": { + "version": "1.0.11", + "from": "dateformat@>=1.0.11 <2.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.11.tgz", + "dependencies": { + "get-stdin": { + "version": "5.0.0", + "from": "get-stdin@*", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.0.tgz" + }, + "meow": { + "version": "3.3.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + } + } + }, + "detect-conflict": { + "version": "1.0.0", + "from": "detect-conflict@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/detect-conflict/-/detect-conflict-1.0.0.tgz" + }, + "diff": { + "version": "2.1.0", + "from": "diff@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-2.1.0.tgz" + }, + "download": { + "version": "4.2.0", + "from": "download@>=4.1.2 <5.0.0", + "resolved": "https://registry.npmjs.org/download/-/download-4.2.0.tgz", + "dependencies": { + "caw": { + "version": "1.1.0", + "from": "caw@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/caw/-/caw-1.1.0.tgz", + "dependencies": { + "get-proxy": { + "version": "1.0.1", + "from": "get-proxy@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-1.0.1.tgz", + "dependencies": { + "rc": { + "version": "0.5.5", + "from": "rc@>=0.5.5 <0.6.0", + "resolved": "https://registry.npmjs.org/rc/-/rc-0.5.5.tgz", + "dependencies": { + "minimist": { + "version": "0.0.10", + "from": "minimist@>=0.0.7 <0.1.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz" + }, + "deep-extend": { + "version": "0.2.11", + "from": "deep-extend@>=0.2.5 <0.3.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.2.11.tgz" + }, + "strip-json-comments": { + "version": "0.1.3", + "from": "strip-json-comments@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-0.1.3.tgz" + }, + "ini": { + "version": "1.3.4", + "from": "ini@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz" + } + } + } + } + }, + "is-obj": { + "version": "1.0.0", + "from": "is-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "tunnel-agent": { + "version": "0.4.1", + "from": "tunnel-agent@>=0.4.0 <0.5.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.1.tgz" + } + } + }, + "concat-stream": { + "version": "1.5.0", + "from": "concat-stream@>=1.4.7 <2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "typedarray": { + "version": "0.0.6", + "from": "typedarray@>=0.0.5 <0.1.0", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "each-async": { + "version": "1.1.1", + "from": "each-async@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/each-async/-/each-async-1.1.1.tgz", + "dependencies": { + "onetime": { + "version": "1.0.0", + "from": "onetime@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.0.0.tgz" + }, + "set-immediate-shim": { + "version": "1.0.1", + "from": "set-immediate-shim@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz" + } + } + }, + "filenamify": { + "version": "1.2.0", + "from": "filenamify@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-1.2.0.tgz", + "dependencies": { + "filename-reserved-regex": { + "version": "1.0.0", + "from": "filename-reserved-regex@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-1.0.0.tgz" + }, + "strip-outer": { + "version": "1.0.0", + "from": "strip-outer@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.0.tgz", + "dependencies": { + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + } + } + }, + "trim-repeated": { + "version": "1.0.0", + "from": "trim-repeated@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "dependencies": { + "escape-string-regexp": { + "version": "1.0.3", + "from": "escape-string-regexp@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.3.tgz" + } + } + } + } + }, + "got": { + "version": "2.9.2", + "from": "got@>=2.3.2 <3.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-2.9.2.tgz", + "dependencies": { + "duplexify": { + "version": "3.4.2", + "from": "duplexify@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.4.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "from": "end-of-stream@1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "infinity-agent": { + "version": "2.0.3", + "from": "infinity-agent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/infinity-agent/-/infinity-agent-2.0.3.tgz" + }, + "is-stream": { + "version": "1.0.1", + "from": "is-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.0.1.tgz" + }, + "lowercase-keys": { + "version": "1.0.0", + "from": "lowercase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" + }, + "nested-error-stacks": { + "version": "1.0.1", + "from": "nested-error-stacks@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.1.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "prepend-http": { + "version": "1.0.3", + "from": "prepend-http@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.3.tgz" + }, + "read-all-stream": { + "version": "2.2.0", + "from": "read-all-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-2.2.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "statuses": { + "version": "1.2.1", + "from": "statuses@>=1.2.1 <2.0.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" + }, + "timed-out": { + "version": "2.0.0", + "from": "timed-out@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz" + } + } + }, + "gulp-decompress": { + "version": "1.1.0", + "from": "gulp-decompress@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/gulp-decompress/-/gulp-decompress-1.1.0.tgz", + "dependencies": { + "archive-type": { + "version": "3.0.1", + "from": "archive-type@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/archive-type/-/archive-type-3.0.1.tgz", + "dependencies": { + "file-type": { + "version": "2.10.2", + "from": "file-type@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-2.10.2.tgz", + "dependencies": { + "meow": { + "version": "3.3.0", + "from": "meow@>=3.3.0 <4.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + } + } + } + } + }, + "decompress": { + "version": "2.3.0", + "from": "decompress@>=2.1.1 <3.0.0", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-2.3.0.tgz", + "dependencies": { + "buffer-to-vinyl": { + "version": "1.0.1", + "from": "buffer-to-vinyl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/buffer-to-vinyl/-/buffer-to-vinyl-1.0.1.tgz", + "dependencies": { + "file-type": { + "version": "2.10.2", + "from": "file-type@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-2.10.2.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "uuid": { + "version": "2.0.1", + "from": "uuid@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz" + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + } + } + }, + "decompress-tar": { + "version": "3.1.0", + "from": "decompress-tar@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-3.1.0.tgz", + "dependencies": { + "is-tar": { + "version": "1.0.0", + "from": "is-tar@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-tar/-/is-tar-1.0.0.tgz" + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "tar-stream": { + "version": "1.2.1", + "from": "tar-stream@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.2.1.tgz", + "dependencies": { + "bl": { + "version": "1.0.0", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.0.tgz" + }, + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "decompress-tarbz2": { + "version": "3.1.0", + "from": "decompress-tarbz2@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-3.1.0.tgz", + "dependencies": { + "is-bzip2": { + "version": "1.0.0", + "from": "is-bzip2@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-bzip2/-/is-bzip2-1.0.0.tgz" + }, + "seek-bzip": { + "version": "1.0.5", + "from": "seek-bzip@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", + "dependencies": { + "commander": { + "version": "2.8.1", + "from": "commander@>=2.8.1 <2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", + "dependencies": { + "graceful-readlink": { + "version": "1.0.1", + "from": "graceful-readlink@>=1.0.0", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz" + } + } + } + } + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "tar-stream": { + "version": "1.2.1", + "from": "tar-stream@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.2.1.tgz", + "dependencies": { + "bl": { + "version": "1.0.0", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.0.tgz" + }, + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "decompress-targz": { + "version": "3.1.0", + "from": "decompress-targz@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-3.1.0.tgz", + "dependencies": { + "is-gzip": { + "version": "1.0.0", + "from": "is-gzip@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz" + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "tar-stream": { + "version": "1.2.1", + "from": "tar-stream@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.2.1.tgz", + "dependencies": { + "bl": { + "version": "1.0.0", + "from": "bl@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.0.tgz" + }, + "end-of-stream": { + "version": "1.1.0", + "from": "end-of-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <5.0.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "decompress-unzip": { + "version": "3.3.0", + "from": "decompress-unzip@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-3.3.0.tgz", + "dependencies": { + "is-zip": { + "version": "1.0.0", + "from": "is-zip@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-zip/-/is-zip-1.0.0.tgz" + }, + "stat-mode": { + "version": "0.2.1", + "from": "stat-mode@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-0.2.1.tgz" + }, + "strip-dirs": { + "version": "1.1.1", + "from": "strip-dirs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-1.1.1.tgz", + "dependencies": { + "is-absolute": { + "version": "0.1.7", + "from": "is-absolute@>=0.1.5 <0.2.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.1.7.tgz", + "dependencies": { + "is-relative": { + "version": "0.1.3", + "from": "is-relative@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-0.1.3.tgz" + } + } + }, + "is-natural-number": { + "version": "2.0.0", + "from": "is-natural-number@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-2.0.0.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "sum-up": { + "version": "1.0.2", + "from": "sum-up@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sum-up/-/sum-up-1.0.2.tgz" + } + } + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + }, + "yauzl": { + "version": "2.3.1", + "from": "yauzl@>=2.2.1 <3.0.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.3.1.tgz", + "dependencies": { + "fd-slicer": { + "version": "1.0.1", + "from": "fd-slicer@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz" + }, + "pend": { + "version": "1.2.0", + "from": "pend@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" + } + } + } + } + }, + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "meow": { + "version": "3.3.0", + "from": "meow@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "vinyl-assign": { + "version": "1.2.0", + "from": "vinyl-assign@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-assign/-/vinyl-assign-1.2.0.tgz", + "dependencies": { + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + } + } + }, + "gulp-util": { + "version": "3.0.6", + "from": "gulp-util@>=3.0.1 <4.0.0", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.6.tgz", + "dependencies": { + "array-differ": { + "version": "1.0.0", + "from": "array-differ@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz" + }, + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + }, + "beeper": { + "version": "1.1.0", + "from": "beeper@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.0.tgz" + }, + "lodash._reescape": { + "version": "3.0.0", + "from": "lodash._reescape@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz" + }, + "lodash._reevaluate": { + "version": "3.0.0", + "from": "lodash._reevaluate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "from": "lodash._reinterpolate@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz" + }, + "lodash.template": { + "version": "3.6.2", + "from": "lodash.template@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "dependencies": { + "lodash._basecopy": { + "version": "3.0.1", + "from": "lodash._basecopy@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz" + }, + "lodash._basetostring": { + "version": "3.0.1", + "from": "lodash._basetostring@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz" + }, + "lodash._basevalues": { + "version": "3.0.0", + "from": "lodash._basevalues@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "from": "lodash._isiterateecall@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz" + }, + "lodash.escape": { + "version": "3.0.0", + "from": "lodash.escape@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.0.0.tgz" + }, + "lodash.keys": { + "version": "3.1.2", + "from": "lodash.keys@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "dependencies": { + "lodash._getnative": { + "version": "3.9.1", + "from": "lodash._getnative@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz" + }, + "lodash.isarguments": { + "version": "3.0.4", + "from": "lodash.isarguments@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.0.4.tgz" + }, + "lodash.isarray": { + "version": "3.0.4", + "from": "lodash.isarray@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz" + } + } + }, + "lodash.restparam": { + "version": "3.6.1", + "from": "lodash.restparam@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz" + }, + "lodash.templatesettings": { + "version": "3.1.0", + "from": "lodash.templatesettings@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.0.tgz" + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "multipipe": { + "version": "0.1.2", + "from": "multipipe@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.1.9 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + } + } + } + } + }, + "gulp-rename": { + "version": "1.2.2", + "from": "gulp-rename@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz" + }, + "is-url": { + "version": "1.2.1", + "from": "is-url@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.1.tgz" + }, + "object-assign": { + "version": "2.1.1", + "from": "object-assign@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz" + }, + "read-all-stream": { + "version": "3.0.1", + "from": "read-all-stream@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.0.1.tgz", + "dependencies": { + "pinkie-promise": { + "version": "1.0.0", + "from": "pinkie-promise@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "dependencies": { + "pinkie": { + "version": "1.0.0", + "from": "pinkie@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "stream-combiner2": { + "version": "1.0.2", + "from": "stream-combiner2@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.0.2.tgz", + "dependencies": { + "duplexer2": { + "version": "0.0.2", + "from": "duplexer2@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "dependencies": { + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.1.9 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + } + } + }, + "through2": { + "version": "0.5.1", + "from": "through2@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.5.1.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.17 <1.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "3.0.0", + "from": "xtend@>=3.0.0 <3.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz" + } + } + } + } + }, + "vinyl": { + "version": "0.4.6", + "from": "vinyl@>=0.4.3 <0.5.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.4.6.tgz", + "dependencies": { + "clone": { + "version": "0.2.0", + "from": "clone@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-0.2.0.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + } + } + }, + "vinyl-fs": { + "version": "1.0.0", + "from": "vinyl-fs@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-1.0.0.tgz", + "dependencies": { + "duplexify": { + "version": "3.4.2", + "from": "duplexify@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.4.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "from": "end-of-stream@1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "glob-stream": { + "version": "4.1.1", + "from": "glob-stream@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-4.1.1.tgz", + "dependencies": { + "glob": { + "version": "4.5.3", + "from": "glob@>=4.3.1 <5.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.5.3.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "ordered-read-streams": { + "version": "0.1.0", + "from": "ordered-read-streams@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-0.1.0.tgz" + }, + "glob2base": { + "version": "0.0.12", + "from": "glob2base@>=0.0.12 <0.0.13", + "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", + "dependencies": { + "find-index": { + "version": "0.1.1", + "from": "find-index@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz" + } + } + }, + "unique-stream": { + "version": "2.2.0", + "from": "unique-stream@>=2.0.2 <3.0.0", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.2.0.tgz", + "dependencies": { + "through2-filter": { + "version": "2.0.0", + "from": "through2-filter@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-2.0.0.tgz", + "dependencies": { + "through2": { + "version": "2.0.0", + "from": "through2@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + } + } + }, + "glob-watcher": { + "version": "0.0.8", + "from": "glob-watcher@>=0.0.8 <0.0.9", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-0.0.8.tgz", + "dependencies": { + "gaze": { + "version": "0.5.1", + "from": "gaze@>=0.5.1 <0.6.0", + "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.5.1.tgz", + "dependencies": { + "globule": { + "version": "0.1.0", + "from": "globule@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/globule/-/globule-0.1.0.tgz", + "dependencies": { + "lodash": { + "version": "1.0.2", + "from": "lodash@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-1.0.2.tgz" + }, + "glob": { + "version": "3.1.21", + "from": "glob@>=3.1.21 <3.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", + "dependencies": { + "graceful-fs": { + "version": "1.2.3", + "from": "graceful-fs@>=1.2.0 <1.3.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz" + }, + "inherits": { + "version": "1.0.2", + "from": "inherits@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz" + } + } + }, + "minimatch": { + "version": "0.2.14", + "from": "minimatch@>=0.2.11 <0.3.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", + "dependencies": { + "lru-cache": { + "version": "2.6.5", + "from": "lru-cache@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.6.5.tgz" + }, + "sigmund": { + "version": "1.0.1", + "from": "sigmund@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" + } + } + } + } + } + } + } + } + }, + "graceful-fs": { + "version": "3.0.8", + "from": "graceful-fs@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.8.tgz" + }, + "merge-stream": { + "version": "0.1.8", + "from": "merge-stream@>=0.1.7 <0.2.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-0.1.8.tgz" + }, + "strip-bom": { + "version": "1.0.0", + "from": "strip-bom@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-1.0.0.tgz", + "dependencies": { + "first-chunk-stream": { + "version": "1.0.0", + "from": "first-chunk-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-1.0.0.tgz" + }, + "is-utf8": { + "version": "0.2.0", + "from": "is-utf8@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.0.tgz" + } + } + }, + "through2": { + "version": "0.6.5", + "from": "through2@>=0.6.1 <0.7.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "dependencies": { + "readable-stream": { + "version": "1.0.33", + "from": "readable-stream@>=1.0.33-1 <1.1.0-0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0-0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + } + } + }, + "ware": { + "version": "1.3.0", + "from": "ware@>=1.2.0 <2.0.0", + "resolved": "https://registry.npmjs.org/ware/-/ware-1.3.0.tgz", + "dependencies": { + "wrap-fn": { + "version": "0.1.4", + "from": "wrap-fn@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/wrap-fn/-/wrap-fn-0.1.4.tgz", + "dependencies": { + "co": { + "version": "3.1.0", + "from": "co@3.1.0", + "resolved": "https://registry.npmjs.org/co/-/co-3.1.0.tgz" + } + } + } + } + } + } + }, + "findup-sync": { + "version": "0.2.1", + "from": "findup-sync@>=0.2.1 <0.3.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.2.1.tgz", + "dependencies": { + "glob": { + "version": "4.3.5", + "from": "glob@>=4.3.0 <4.4.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-4.3.5.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + } + } + }, + "github-username": { + "version": "2.0.0", + "from": "github-username@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-2.0.0.tgz", + "dependencies": { + "gh-got": { + "version": "1.1.0", + "from": "gh-got@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-1.1.0.tgz", + "dependencies": { + "got": { + "version": "3.3.1", + "from": "got@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/got/-/got-3.3.1.tgz", + "dependencies": { + "duplexify": { + "version": "3.4.2", + "from": "duplexify@>=3.2.0 <4.0.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.4.2.tgz", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "from": "end-of-stream@1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "infinity-agent": { + "version": "2.0.3", + "from": "infinity-agent@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/infinity-agent/-/infinity-agent-2.0.3.tgz" + }, + "is-redirect": { + "version": "1.0.0", + "from": "is-redirect@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz" + }, + "is-stream": { + "version": "1.0.1", + "from": "is-stream@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.0.1.tgz" + }, + "lowercase-keys": { + "version": "1.0.0", + "from": "lowercase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" + }, + "nested-error-stacks": { + "version": "1.0.1", + "from": "nested-error-stacks@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.1.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + }, + "prepend-http": { + "version": "1.0.3", + "from": "prepend-http@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.3.tgz" + }, + "read-all-stream": { + "version": "3.0.1", + "from": "read-all-stream@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/read-all-stream/-/read-all-stream-3.0.1.tgz", + "dependencies": { + "pinkie-promise": { + "version": "1.0.0", + "from": "pinkie-promise@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-1.0.0.tgz", + "dependencies": { + "pinkie": { + "version": "1.0.0", + "from": "pinkie@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-1.0.0.tgz" + } + } + }, + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + } + } + }, + "timed-out": { + "version": "2.0.0", + "from": "timed-out@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz" + } + } + }, + "object-assign": { + "version": "2.1.1", + "from": "object-assign@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-2.1.1.tgz" + } + } + }, + "meow": { + "version": "3.3.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz", + "dependencies": { + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + } + } + }, + "glob": { + "version": "5.0.14", + "from": "glob@>=5.0.3 <6.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.14.tgz", + "dependencies": { + "inflight": { + "version": "1.0.4", + "from": "inflight@>=1.0.4 <2.0.0", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.4.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + }, + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "gruntfile-editor": { + "version": "1.0.0", + "from": "gruntfile-editor@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/gruntfile-editor/-/gruntfile-editor-1.0.0.tgz", + "dependencies": { + "ast-query": { + "version": "1.0.1", + "from": "ast-query@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ast-query/-/ast-query-1.0.1.tgz", + "dependencies": { + "escodegen": { + "version": "1.6.1", + "from": "escodegen@>=1.6.0 <2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.6.1.tgz", + "dependencies": { + "estraverse": { + "version": "1.9.3", + "from": "estraverse@>=1.9.1 <2.0.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz" + }, + "esutils": { + "version": "1.1.6", + "from": "esutils@>=1.1.6 <2.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz" + }, + "esprima": { + "version": "1.2.5", + "from": "esprima@>=1.2.2 <2.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz" + }, + "optionator": { + "version": "0.5.0", + "from": "optionator@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.5.0.tgz", + "dependencies": { + "prelude-ls": { + "version": "1.1.2", + "from": "prelude-ls@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + }, + "deep-is": { + "version": "0.1.3", + "from": "deep-is@>=0.1.2 <0.2.0", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" + }, + "wordwrap": { + "version": "0.0.3", + "from": "wordwrap@>=0.0.2 <0.1.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz" + }, + "type-check": { + "version": "0.3.1", + "from": "type-check@>=0.3.1 <0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.1.tgz" + }, + "levn": { + "version": "0.2.5", + "from": "levn@>=0.2.5 <0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.2.5.tgz" + }, + "fast-levenshtein": { + "version": "1.0.7", + "from": "fast-levenshtein@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz" + } + } + }, + "source-map": { + "version": "0.1.43", + "from": "source-map@>=0.1.40 <0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "dependencies": { + "amdefine": { + "version": "1.0.0", + "from": "amdefine@>=0.0.4", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.0.tgz" + } + } + } + } + }, + "esprima": { + "version": "2.6.0", + "from": "esprima@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.6.0.tgz" + }, + "traverse": { + "version": "0.6.6", + "from": "traverse@>=0.6.6 <0.7.0", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz" + } + } + } + } + }, + "html-wiring": { + "version": "1.2.0", + "from": "html-wiring@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/html-wiring/-/html-wiring-1.2.0.tgz", + "dependencies": { + "cheerio": { + "version": "0.19.0", + "from": "cheerio@>=0.19.0 <0.20.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.19.0.tgz", + "dependencies": { + "css-select": { + "version": "1.0.0", + "from": "css-select@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.0.0.tgz", + "dependencies": { + "css-what": { + "version": "1.0.0", + "from": "css-what@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-1.0.0.tgz" + }, + "domutils": { + "version": "1.4.3", + "from": "domutils@>=1.4.0 <1.5.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.4.3.tgz", + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + } + } + }, + "boolbase": { + "version": "1.0.0", + "from": "boolbase@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + }, + "nth-check": { + "version": "1.0.1", + "from": "nth-check@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz" + } + } + }, + "entities": { + "version": "1.1.1", + "from": "entities@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz" + }, + "htmlparser2": { + "version": "3.8.3", + "from": "htmlparser2@>=3.8.1 <3.9.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "dependencies": { + "domhandler": { + "version": "2.3.0", + "from": "domhandler@>=2.3.0 <2.4.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz" + }, + "domutils": { + "version": "1.5.1", + "from": "domutils@>=1.5.0 <1.6.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz" + }, + "domelementtype": { + "version": "1.3.0", + "from": "domelementtype@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz" + }, + "readable-stream": { + "version": "1.1.13", + "from": "readable-stream@>=1.1.0 <1.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "entities": { + "version": "1.0.0", + "from": "entities@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz" + } + } + }, + "dom-serializer": { + "version": "0.1.0", + "from": "dom-serializer@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", + "dependencies": { + "domelementtype": { + "version": "1.1.3", + "from": "domelementtype@>=1.1.1 <1.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz" + } + } + } + } + }, + "detect-newline": { + "version": "1.0.3", + "from": "detect-newline@>=1.0.3 <2.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-1.0.3.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + } + } + } + } + }, + "inquirer": { + "version": "0.8.5", + "from": "inquirer@>=0.8.0 <0.9.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.8.5.tgz", + "dependencies": { + "ansi-regex": { + "version": "1.1.1", + "from": "ansi-regex@>=1.1.1 <2.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-1.1.1.tgz" + }, + "cli-width": { + "version": "1.0.1", + "from": "cli-width@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-1.0.1.tgz" + }, + "figures": { + "version": "1.3.5", + "from": "figures@>=1.3.5 <2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.3.5.tgz" + }, + "readline2": { + "version": "0.1.1", + "from": "readline2@>=0.1.1 <0.2.0", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-0.1.1.tgz", + "dependencies": { + "mute-stream": { + "version": "0.0.4", + "from": "mute-stream@0.0.4", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.4.tgz" + }, + "strip-ansi": { + "version": "2.0.1", + "from": "strip-ansi@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-2.0.1.tgz" + } + } + }, + "rx": { + "version": "2.5.3", + "from": "rx@>=2.4.3 <3.0.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-2.5.3.tgz" + }, + "through": { + "version": "2.3.8", + "from": "through@>=2.3.6 <3.0.0", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + } + } + }, + "istextorbinary": { + "version": "1.0.2", + "from": "istextorbinary@>=1.0.2 <2.0.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz", + "dependencies": { + "textextensions": { + "version": "1.0.1", + "from": "textextensions@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.1.tgz" + }, + "binaryextensions": { + "version": "1.0.0", + "from": "binaryextensions@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.0.tgz" + } + } + }, + "lodash": { + "version": "3.10.1", + "from": "lodash@>=3.5.0 <4.0.0", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz" + }, + "mem-fs-editor": { + "version": "2.0.4", + "from": "mem-fs-editor@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-2.0.4.tgz", + "dependencies": { + "commondir": { + "version": "1.0.1", + "from": "commondir@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + }, + "ejs": { + "version": "2.3.3", + "from": "ejs@>=2.3.1 <3.0.0", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.3.3.tgz" + }, + "globby": { + "version": "2.1.0", + "from": "globby@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-2.1.0.tgz", + "dependencies": { + "array-union": { + "version": "1.0.1", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.1.tgz", + "dependencies": { + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + } + } + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "multimatch": { + "version": "2.0.0", + "from": "multimatch@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.0.0.tgz", + "dependencies": { + "array-differ": { + "version": "1.0.0", + "from": "array-differ@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz" + }, + "array-union": { + "version": "1.0.1", + "from": "array-union@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.1.tgz", + "dependencies": { + "array-uniq": { + "version": "1.0.2", + "from": "array-uniq@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.2.tgz" + } + } + }, + "minimatch": { + "version": "2.0.10", + "from": "minimatch@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-2.0.10.tgz", + "dependencies": { + "brace-expansion": { + "version": "1.1.0", + "from": "brace-expansion@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.0.tgz", + "dependencies": { + "balanced-match": { + "version": "0.2.0", + "from": "balanced-match@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.2.0.tgz" + }, + "concat-map": { + "version": "0.0.1", + "from": "concat-map@0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + } + } + } + } + } + } + }, + "vinyl": { + "version": "0.5.3", + "from": "vinyl@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "dependencies": { + "clone": { + "version": "1.0.2", + "from": "clone@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz" + }, + "clone-stats": { + "version": "0.0.1", + "from": "clone-stats@>=0.0.1 <0.0.2", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz" + }, + "replace-ext": { + "version": "0.0.1", + "from": "replace-ext@0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz" + } + } + } + } + }, + "mime": { + "version": "1.3.4", + "from": "mime@>=1.2.9 <2.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" + }, + "mkdirp": { + "version": "0.5.1", + "from": "mkdirp@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "dependencies": { + "minimist": { + "version": "0.0.8", + "from": "minimist@0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" + } + } + }, + "nopt": { + "version": "3.0.3", + "from": "nopt@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.3.tgz", + "dependencies": { + "abbrev": { + "version": "1.0.7", + "from": "abbrev@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.7.tgz" + } + } + }, + "path-exists": { + "version": "1.0.0", + "from": "path-exists@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz" + }, + "path-is-absolute": { + "version": "1.0.0", + "from": "path-is-absolute@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.0.tgz" + }, + "pretty-bytes": { + "version": "2.0.1", + "from": "pretty-bytes@>=2.0.1 <3.0.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-2.0.1.tgz", + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "from": "get-stdin@>=4.0.1 <5.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz" + }, + "meow": { + "version": "3.3.0", + "from": "meow@*", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.3.0.tgz", + "dependencies": { + "camelcase-keys": { + "version": "1.0.0", + "from": "camelcase-keys@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-1.0.0.tgz", + "dependencies": { + "camelcase": { + "version": "1.2.1", + "from": "camelcase@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz" + }, + "map-obj": { + "version": "1.0.1", + "from": "map-obj@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + } + } + }, + "indent-string": { + "version": "1.2.2", + "from": "indent-string@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-1.2.2.tgz", + "dependencies": { + "repeating": { + "version": "1.1.3", + "from": "repeating@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-1.1.3.tgz", + "dependencies": { + "is-finite": { + "version": "1.0.1", + "from": "is-finite@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.1.tgz" + } + } + } + } + }, + "minimist": { + "version": "1.2.0", + "from": "minimist@>=1.1.0 <2.0.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz" + }, + "object-assign": { + "version": "3.0.0", + "from": "object-assign@>=3.0.0 <4.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz" + } + } + }, + "number-is-nan": { + "version": "1.0.0", + "from": "number-is-nan@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.0.tgz" + } + } + }, + "read-chunk": { + "version": "1.0.1", + "from": "read-chunk@>=1.0.1 <2.0.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz" + }, + "rimraf": { + "version": "2.4.3", + "from": "rimraf@>=2.2.0 <3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.3.tgz" + }, + "run-async": { + "version": "0.1.0", + "from": "run-async@>=0.1.0 <0.2.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "dependencies": { + "once": { + "version": "1.3.2", + "from": "once@>=1.3.0 <2.0.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.3.2.tgz", + "dependencies": { + "wrappy": { + "version": "1.0.1", + "from": "wrappy@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.1.tgz" + } + } + } + } + }, + "shelljs": { + "version": "0.5.3", + "from": "shelljs@>=0.5.0 <0.6.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz" + }, + "sinon": { + "version": "1.16.1", + "from": "sinon@>=1.9.1 <2.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.16.1.tgz", + "dependencies": { + "formatio": { + "version": "1.1.1", + "from": "formatio@1.1.1", + "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz" + }, + "util": { + "version": "0.10.3", + "from": "util@>=0.10.3 <1.0.0", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "dependencies": { + "inherits": { + "version": "2.0.1", + "from": "inherits@2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + } + } + }, + "lolex": { + "version": "1.3.1", + "from": "lolex@1.3.1", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.1.tgz" + }, + "samsam": { + "version": "1.1.2", + "from": "samsam@1.1.2", + "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" + } + } + }, + "text-table": { + "version": "0.2.0", + "from": "text-table@>=0.2.0 <0.3.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + }, + "through2": { + "version": "2.0.0", + "from": "through2@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.0.tgz", + "dependencies": { + "readable-stream": { + "version": "2.0.2", + "from": "readable-stream@>=2.0.0 <2.1.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.2.tgz", + "dependencies": { + "core-util-is": { + "version": "1.0.1", + "from": "core-util-is@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" + }, + "inherits": { + "version": "2.0.1", + "from": "inherits@>=2.0.1 <2.1.0", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" + }, + "isarray": { + "version": "0.0.1", + "from": "isarray@0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" + }, + "process-nextick-args": { + "version": "1.0.2", + "from": "process-nextick-args@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.2.tgz" + }, + "string_decoder": { + "version": "0.10.31", + "from": "string_decoder@>=0.10.0 <0.11.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" + }, + "util-deprecate": { + "version": "1.0.1", + "from": "util-deprecate@>=1.0.1 <1.1.0", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.1.tgz" + } + } + }, + "xtend": { + "version": "4.0.0", + "from": "xtend@>=4.0.0 <4.1.0", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz" + } + } + }, + "underscore.string": { + "version": "3.2.1", + "from": "underscore.string@>=3.0.3 <4.0.0", + "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.2.1.tgz" + }, + "user-home": { + "version": "2.0.0", + "from": "user-home@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.1", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" + } + } + }, + "xdg-basedir": { + "version": "2.0.0", + "from": "xdg-basedir@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz", + "dependencies": { + "os-homedir": { + "version": "1.0.1", + "from": "os-homedir@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.1.tgz" + } + } + }, + "yeoman-assert": { + "version": "2.0.0", + "from": "yeoman-assert@>=2.0.0 <3.0.0", + "resolved": "https://registry.npmjs.org/yeoman-assert/-/yeoman-assert-2.0.0.tgz" + }, + "yeoman-welcome": { + "version": "1.0.1", + "from": "yeoman-welcome@>=1.0.0 <2.0.0", + "resolved": "https://registry.npmjs.org/yeoman-welcome/-/yeoman-welcome-1.0.1.tgz" + } + } + } + } +}