diff --git a/.eslintrc b/.eslintrc index 8de521e66..a81fb56f3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -37,7 +37,7 @@ "rules": { "no-cond-assign": 1, // disallow assignment in conditional expressions "no-console": 0, // disallow use of console (off by default in the node environment) - "no-constant-condition": 1, // disallow use of constant expressions in conditions + "no-constant-condition": 0, // disallow use of constant expressions in conditions "no-comma-dangle": 0, // disallow trailing commas in object literals "no-control-regex": 1, // disallow control characters in regular expressions "no-debugger": 1, // disallow use of debugger diff --git a/Examples/2048/2048.xcodeproj/project.pbxproj b/Examples/2048/2048.xcodeproj/project.pbxproj index 18f1ec2a9..d792a2d62 100644 --- a/Examples/2048/2048.xcodeproj/project.pbxproj +++ b/Examples/2048/2048.xcodeproj/project.pbxproj @@ -7,23 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 13ACB6741AC2117000FF4204 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13ACB6711AC2113600FF4204 /* libRCTAnimation.a */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 1461632D1AC3E23900C2F5AD /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1461632C1AC3E22900C2F5AD /* libReact.a */; }; + 58C1E40E1ACF54E9006D1A47 /* libRCTAnimationExperimental.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C1E40B1ACF54B4006D1A47 /* libRCTAnimationExperimental.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 13ACB6701AC2113600FF4204 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 134814201AA4EA6300B7C361; - remoteInfo = RCTAnimation; - }; 1461632B1AC3E22900C2F5AD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 146163271AC3E22900C2F5AD /* React.xcodeproj */; @@ -31,6 +24,13 @@ remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; remoteInfo = React; }; + 58C1E40A1ACF54B4006D1A47 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTAnimationExperimental; + }; 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; @@ -41,7 +41,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = ../../Libraries/Animation/RCTAnimation.xcodeproj; sourceTree = ""; }; + 13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimationExperimental.xcodeproj; path = ../../Libraries/Animation/RCTAnimationExperimental.xcodeproj; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* 2048.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = 2048.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = 2048/AppDelegate.h; sourceTree = ""; }; 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = 2048/AppDelegate.m; sourceTree = ""; }; @@ -59,7 +59,7 @@ buildActionMask = 2147483647; files = ( 1461632D1AC3E23900C2F5AD /* libReact.a in Frameworks */, - 13ACB6741AC2117000FF4204 /* libRCTAnimation.a in Frameworks */, + 58C1E40E1ACF54E9006D1A47 /* libRCTAnimationExperimental.a in Frameworks */, 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -67,14 +67,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 13ACB66D1AC2113500FF4204 /* Products */ = { - isa = PBXGroup; - children = ( - 13ACB6711AC2113600FF4204 /* libRCTAnimation.a */, - ); - name = Products; - sourceTree = ""; - }; 13B07FAE1A68108700A75B9A /* 2048 */ = { isa = PBXGroup; children = ( @@ -96,12 +88,20 @@ name = Products; sourceTree = ""; }; + 58C1E4071ACF54B4006D1A47 /* Products */ = { + isa = PBXGroup; + children = ( + 58C1E40B1ACF54B4006D1A47 /* libRCTAnimationExperimental.a */, + ); + name = Products; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( 146163271AC3E22900C2F5AD /* React.xcodeproj */, 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, - 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */, + 13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */, ); name = Libraries; sourceTree = ""; @@ -175,8 +175,8 @@ projectDirPath = ""; projectReferences = ( { - ProductGroup = 13ACB66D1AC2113500FF4204 /* Products */; - ProjectRef = 13ACB66C1AC2113500FF4204 /* RCTAnimation.xcodeproj */; + ProductGroup = 58C1E4071ACF54B4006D1A47 /* Products */; + ProjectRef = 13ACB66C1AC2113500FF4204 /* RCTAnimationExperimental.xcodeproj */; }, { ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; @@ -195,13 +195,6 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 13ACB6711AC2113600FF4204 /* libRCTAnimation.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRCTAnimation.a; - remoteRef = 13ACB6701AC2113600FF4204 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 1461632C1AC3E22900C2F5AD /* libReact.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -209,6 +202,13 @@ remoteRef = 1461632B1AC3E22900C2F5AD /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 58C1E40B1ACF54B4006D1A47 /* libRCTAnimationExperimental.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimationExperimental.a; + remoteRef = 58C1E40A1ACF54B4006D1A47 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -266,6 +266,10 @@ ); INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Animation/build/Debug-iphoneos", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = 2048; }; @@ -282,6 +286,10 @@ ); INFOPLIST_FILE = "$(SRCROOT)/2048/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "/Users/sahrens/src/fbobjc-hg/Libraries/FBReactKit/js/react-native-github/Libraries/Animation/build/Debug-iphoneos", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = 2048; }; diff --git a/Examples/2048/Game2048.js b/Examples/2048/Game2048.js index 03abfcd69..a6e12ff13 100644 --- a/Examples/2048/Game2048.js +++ b/Examples/2048/Game2048.js @@ -18,13 +18,13 @@ var React = require('react-native'); var { - Animation, AppRegistry, StyleSheet, Text, View, } = React; +var AnimationExperimental = require('AnimationExperimental'); var GameBoard = require('GameBoard'); var TouchableBounce = require('TouchableBounce'); @@ -77,17 +77,15 @@ class Tile extends React.Component { animationPosition(tile.toColumn()), animationPosition(tile.toRow()), ]; - Animation.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {position: point}); + AnimationExperimental.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {position: point}); } return offset; } + componentDidMount() { - setTimeout(() => { - Animation.startAnimation(this.refs['this'], 300, 0, 'easeInOutQuad', {scaleXY: [1, 1]}); - Animation.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {opacity: 1}); - }, 0); + AnimationExperimental.startAnimation(this.refs['this'], 100, 0, 'easeInOutQuad', {opacity: 1}); } render() { diff --git a/Examples/UIExplorer/Navigator/JumpingNavSample.js b/Examples/UIExplorer/Navigator/JumpingNavSample.js index b38d9291d..eecabcbce 100644 --- a/Examples/UIExplorer/Navigator/JumpingNavSample.js +++ b/Examples/UIExplorer/Navigator/JumpingNavSample.js @@ -52,26 +52,45 @@ var ROUTE_STACK = [ var INIT_ROUTE_INDEX = 1; class JumpingNavBar extends React.Component { + constructor(props) { + super(props); + this.state = { + tabIndex: props.initTabIndex, + }; + } + handleWillFocus(route) { + var tabIndex = ROUTE_STACK.indexOf(route); + this.setState({ tabIndex, }); + } render() { return ( { this.props.onTabIndex(0); }}> + selected={this.state.tabIndex === 0} + onPress={() => { + this.props.onTabIndex(0); + this.setState({ tabIndex: 0, }); + }}> { this.props.onTabIndex(1); }}> + selected={this.state.tabIndex === 1} + onPress={() => { + this.props.onTabIndex(1); + this.setState({ tabIndex: 1, }); + }}> { this.props.onTabIndex(2); }}> + selected={this.state.tabIndex === 2} + onPress={() => { + this.props.onTabIndex(2); + this.setState({ tabIndex: 2, }); + }}> @@ -81,12 +100,6 @@ class JumpingNavBar extends React.Component { } var JumpingNavSample = React.createClass({ - getInitialState: function() { - return { - tabIndex: INIT_ROUTE_INDEX, - }; - }, - render: function() { return ( ({ + ...Navigator.SceneConfigs.HorizontalSwipeJump, + })} navigationBar={ { this.navBar = navBar; }} + initTabIndex={INIT_ROUTE_INDEX} routeStack={ROUTE_STACK} - tabIndex={this.state.tabIndex} onTabIndex={(index) => { - this.setState({ tabIndex: index }, () => { - this._navigator.jumpTo(ROUTE_STACK[index]); - }); + this._navigator.jumpTo(ROUTE_STACK[index]); }} /> } - onWillFocus={(route) => { - this.setState({ - tabIndex: ROUTE_STACK.indexOf(route), - }); - }} - shouldJumpOnBackstackPop={true} /> ); }, diff --git a/Examples/UIExplorer/NavigatorIOSExample.js b/Examples/UIExplorer/NavigatorIOSExample.js index a7acdde24..eee731bd5 100644 --- a/Examples/UIExplorer/NavigatorIOSExample.js +++ b/Examples/UIExplorer/NavigatorIOSExample.js @@ -17,6 +17,7 @@ var React = require('react-native'); var ViewExample = require('./ViewExample'); +var createExamplePage = require('./createExamplePage'); var { PixelRatio, ScrollView, @@ -77,7 +78,7 @@ var NavigatorIOSExample = React.createClass({ {this._renderRow('Push View Example', () => { this.props.navigator.push({ title: 'Very Long Custom View Example Title', - component: ViewExample, + component: createExamplePage(null, ViewExample), }); })} {this._renderRow('Custom Right Button', () => { diff --git a/Examples/UIExplorer/ResponderExample.js b/Examples/UIExplorer/ResponderExample.js index 4cc62ff3a..05ba27cd2 100644 --- a/Examples/UIExplorer/ResponderExample.js +++ b/Examples/UIExplorer/ResponderExample.js @@ -38,7 +38,7 @@ var NavigatorIOSExample = React.createClass({ _previousLeft: 0, _previousTop: 0, _circleStyles: {}, - circle: (null : ?React.Element), + circle: (null : ?{ setNativeProps(props: Object): void }), componentWillMount: function() { this._panResponder = PanResponder.create({ diff --git a/Libraries/Animation/Animation.js b/Libraries/Animation/AnimationExperimental.js similarity index 56% rename from Libraries/Animation/Animation.js rename to Libraries/Animation/AnimationExperimental.js index 019802f5e..79daa4550 100644 --- a/Libraries/Animation/Animation.js +++ b/Libraries/Animation/AnimationExperimental.js @@ -6,18 +6,25 @@ * 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. * - * @providesModule Animation + * @providesModule AnimationExperimental * @flow */ 'use strict'; -var RCTAnimationManager = require('NativeModules').AnimationManager; +var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager; var AnimationUtils = require('AnimationUtils'); type EasingFunction = (t: number) => number; -var Animation = { - Mixin: require('AnimationMixin'), +/** + * This is an experimental module that is under development, incomplete, + * potentially buggy, not used in any production apps, and will probably change + * in non-backward compatible ways. + * + * Use at your own risk. + */ +var AnimationExperimental = { + Mixin: require('AnimationExperimentalMixin'), startAnimation: function( node: any, @@ -28,7 +35,14 @@ var Animation = { ): number { var nodeHandle = +node.getNodeHandle(); var easingSample = AnimationUtils.evaluateEasingFunction(duration, easing); - var tag: number = RCTAnimationManager.startAnimation(nodeHandle, AnimationUtils.allocateTag(), duration, delay, easingSample, properties); + var tag: number = RCTAnimationManager.startAnimation( + nodeHandle, + AnimationUtils.allocateTag(), + duration, + delay, + easingSample, + properties + ); return tag; }, @@ -37,4 +51,4 @@ var Animation = { }, }; -module.exports = Animation; +module.exports = AnimationExperimental; diff --git a/Libraries/Animation/AnimationMixin.js b/Libraries/Animation/AnimationExperimentalMixin.js similarity index 74% rename from Libraries/Animation/AnimationMixin.js rename to Libraries/Animation/AnimationExperimentalMixin.js index 8cb9680ef..7cee9b72b 100644 --- a/Libraries/Animation/AnimationMixin.js +++ b/Libraries/Animation/AnimationExperimentalMixin.js @@ -6,19 +6,26 @@ * 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. * - * @providesModule AnimationMixin + * @providesModule AnimationExperimentalMixin * @flow */ 'use strict'; var AnimationUtils = require('AnimationUtils'); -var RCTAnimationManager = require('NativeModules').AnimationManager; +var RCTAnimationManager = require('NativeModules').AnimationExperimentalManager; var invariant = require('invariant'); type EasingFunction = (t: number) => number; -var AnimationMixin = { +/** + * This is an experimental module that is under development, incomplete, + * potentially buggy, not used in any production apps, and will probably change + * in non-backward compatible ways. + * + * Use at your own risk. + */ +var AnimationExperimentalMixin = { getInitialState: function(): Object { return {}; }, @@ -48,4 +55,4 @@ var AnimationMixin = { }, }; -module.exports = AnimationMixin; +module.exports = AnimationExperimentalMixin; diff --git a/Libraries/Animation/RCTAnimation.xcodeproj/project.pbxproj b/Libraries/Animation/RCTAnimationExperimental.xcodeproj/project.pbxproj similarity index 80% rename from Libraries/Animation/RCTAnimation.xcodeproj/project.pbxproj rename to Libraries/Animation/RCTAnimationExperimental.xcodeproj/project.pbxproj index 81eb52c70..038ec7252 100644 --- a/Libraries/Animation/RCTAnimation.xcodeproj/project.pbxproj +++ b/Libraries/Animation/RCTAnimationExperimental.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - 13BE3DEE1AC21097009241FE /* RCTAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationManager.m */; }; + 13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -23,9 +23,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 134814201AA4EA6300B7C361 /* libRCTAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 13BE3DEC1AC21097009241FE /* RCTAnimationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationManager.h; sourceTree = ""; }; - 13BE3DED1AC21097009241FE /* RCTAnimationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationManager.m; sourceTree = ""; }; + 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTAnimationExperimental.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAnimationExperimentalManager.h; sourceTree = ""; }; + 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAnimationExperimentalManager.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -42,7 +42,7 @@ 134814211AA4EA7D00B7C361 /* Products */ = { isa = PBXGroup; children = ( - 134814201AA4EA6300B7C361 /* libRCTAnimation.a */, + 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */, ); name = Products; sourceTree = ""; @@ -50,8 +50,8 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - 13BE3DEC1AC21097009241FE /* RCTAnimationManager.h */, - 13BE3DED1AC21097009241FE /* RCTAnimationManager.m */, + 13BE3DEC1AC21097009241FE /* RCTAnimationExperimentalManager.h */, + 13BE3DED1AC21097009241FE /* RCTAnimationExperimentalManager.m */, 134814211AA4EA7D00B7C361 /* Products */, ); sourceTree = ""; @@ -59,9 +59,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 58B511DA1A9E6C8500147676 /* RCTAnimation */ = { + 58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */ = { isa = PBXNativeTarget; - buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimation" */; + buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */; buildPhases = ( 58B511D71A9E6C8500147676 /* Sources */, 58B511D81A9E6C8500147676 /* Frameworks */, @@ -71,9 +71,9 @@ ); dependencies = ( ); - name = RCTAnimation; + name = RCTAnimationExperimental; productName = RCTDataManager; - productReference = 134814201AA4EA6300B7C361 /* libRCTAnimation.a */; + productReference = 134814201AA4EA6300B7C361 /* libRCTAnimationExperimental.a */; productType = "com.apple.product-type.library.static"; }; /* End PBXNativeTarget section */ @@ -90,7 +90,7 @@ }; }; }; - buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimation" */; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 0; @@ -102,7 +102,7 @@ projectDirPath = ""; projectRoot = ""; targets = ( - 58B511DA1A9E6C8500147676 /* RCTAnimation */, + 58B511DA1A9E6C8500147676 /* RCTAnimationExperimental */, ); }; /* End PBXProject section */ @@ -112,7 +112,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 13BE3DEE1AC21097009241FE /* RCTAnimationManager.m in Sources */, + 13BE3DEE1AC21097009241FE /* RCTAnimationExperimentalManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -203,7 +203,7 @@ ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = RCTAnimation; + PRODUCT_NAME = RCTAnimationExperimental; SKIP_INSTALL = YES; }; name = Debug; @@ -218,7 +218,7 @@ ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; - PRODUCT_NAME = RCTAnimation; + PRODUCT_NAME = RCTAnimationExperimental; SKIP_INSTALL = YES; }; name = Release; @@ -226,7 +226,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimation" */ = { + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTAnimationExperimental" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511ED1A9E6C8500147676 /* Debug */, @@ -235,7 +235,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimation" */ = { + 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTAnimationExperimental" */ = { isa = XCConfigurationList; buildConfigurations = ( 58B511F01A9E6C8500147676 /* Debug */, diff --git a/Libraries/Animation/RCTAnimationManager.h b/Libraries/Animation/RCTAnimationExperimentalManager.h similarity index 85% rename from Libraries/Animation/RCTAnimationManager.h rename to Libraries/Animation/RCTAnimationExperimentalManager.h index 1211143da..0affc5233 100644 --- a/Libraries/Animation/RCTAnimationManager.h +++ b/Libraries/Animation/RCTAnimationExperimentalManager.h @@ -12,6 +12,6 @@ #import "RCTBridgeModule.h" -@interface RCTAnimationManager : NSObject +@interface RCTAnimationExperimentalManager : NSObject @end diff --git a/Libraries/Animation/RCTAnimationManager.m b/Libraries/Animation/RCTAnimationExperimentalManager.m similarity index 94% rename from Libraries/Animation/RCTAnimationManager.m rename to Libraries/Animation/RCTAnimationExperimentalManager.m index a6e342db6..88bb5fe28 100644 --- a/Libraries/Animation/RCTAnimationManager.m +++ b/Libraries/Animation/RCTAnimationExperimentalManager.m @@ -7,7 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ -#import "RCTAnimationManager.h" +#import "RCTAnimationExperimentalManager.h" #import @@ -20,7 +20,7 @@ #define CG_APPEND(PREFIX, SUFFIX_F, SUFFIX_D) PREFIX##SUFFIX_F #endif -@implementation RCTAnimationManager +@implementation RCTAnimationExperimentalManager { RCTSparseArray *_animationRegistry; // Main thread only; animation tag -> view tag } @@ -65,9 +65,9 @@ { RCT_EXPORT(startAnimation); - __weak RCTAnimationManager *weakSelf = self; + __weak RCTAnimationExperimentalManager *weakSelf = self; [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { - RCTAnimationManager *strongSelf = weakSelf; + RCTAnimationExperimentalManager *strongSelf = weakSelf; UIView *view = viewRegistry[reactTag]; if (!view) { @@ -182,9 +182,9 @@ { RCT_EXPORT(stopAnimation); - __weak RCTAnimationManager *weakSelf = self; + __weak RCTAnimationExperimentalManager *weakSelf = self; [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) { - RCTAnimationManager *strongSelf = weakSelf; + RCTAnimationExperimentalManager *strongSelf = weakSelf; NSNumber *reactTag = strongSelf->_animationRegistry[animationTag]; if (!reactTag) return; diff --git a/Libraries/Components/ScrollResponder.js b/Libraries/Components/ScrollResponder.js index 009bbebb4..1f3d493f3 100644 --- a/Libraries/Components/ScrollResponder.js +++ b/Libraries/Components/ScrollResponder.js @@ -364,10 +364,16 @@ var ScrollResponderMixin = { /** * This method should be used as the callback to onFocus in a TextInputs' * parent view. Note that any module using this mixin needs to return - * the parent view's ref in getScrollViewRef() in order to use this method + * the parent view's ref in getScrollViewRef() in order to use this method. + * @param {any} nodeHandle The TextInput node handle + * @param {number} additionalOffset The scroll view's top "contentInset". + * Default is 0. + * @param {bool} preventNegativeScrolling Whether to allow pulling the content + * down to make it meet the keyboard's top. Default is false. */ - scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle: any, additionalOffset?: number) { + scrollResponderScrollNativeHandleToKeyboard: function(nodeHandle: any, additionalOffset?: number, preventNegativeScrollOffset?: bool) { this.additionalScrollOffset = additionalOffset || 0; + this.preventNegativeScrollOffset = !!preventNegativeScrollOffset; RCTUIManager.measureLayout( nodeHandle, this.getNodeHandle(), @@ -386,14 +392,23 @@ var ScrollResponderMixin = { * @param {number} width Width of the text input. * @param {number} height Height of the text input. */ - scrollResponderInputMeasureAndScrollToKeyboard: function(left: number, top: number, width: number, height: number) { + scrollResponderInputMeasureAndScrollToKeyboard: function(left: number, top: number, width: number, height: number) { if (this.keyboardWillOpenTo) { var scrollOffsetY = top - this.keyboardWillOpenTo.endCoordinates.screenY + height + this.additionalScrollOffset; + + // By default, this can scroll with negative offset, pulling the content + // down so that the target component's bottom meets the keyboard's top. + // If requested otherwise, cap the offset at 0 minimum to avoid content + // shifting down. + if (this.preventNegativeScrollOffset) { + scrollOffsetY = Math.max(0, scrollOffsetY); + } this.scrollResponderScrollTo(0, scrollOffsetY); } this.additionalOffset = 0; + this.preventNegativeScrollOffset = false; }, scrollResponderTextInputFocusError: function(e: Event) { diff --git a/Libraries/Components/SliderIOS/SliderIOS.js b/Libraries/Components/SliderIOS/SliderIOS.js index a8f2f5125..ae2475d33 100644 --- a/Libraries/Components/SliderIOS/SliderIOS.js +++ b/Libraries/Components/SliderIOS/SliderIOS.js @@ -40,7 +40,7 @@ var SliderIOS = React.createClass({ * Default value is 0. * * *This is not a controlled component*, e.g. if you don't update - * the value, the component won't be reset to it's inital value. + * the value, the component won't be reset to its inital value. */ value: PropTypes.number, @@ -82,6 +82,8 @@ var SliderIOS = React.createClass({ ); @@ -94,8 +96,15 @@ var styles = StyleSheet.create({ }, }); +var validAttributes = { + ...ReactIOSViewAttributes.UIView, + value: true, + minimumValue: true, + maximumValue: true, +}; + var RCTSlider = createReactIOSNativeComponentClass({ - validAttributes: merge(ReactIOSViewAttributes.UIView, {value: true}), + validAttributes: validAttributes, uiViewClassName: 'RCTSlider', }); diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 0b30fd7a5..dc29af70a 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -217,8 +217,6 @@ var TextInput = React.createClass({ */ onFocus: PropTypes.func, /** - * (text: string) => void - * * Callback that is called when the text input's text changes. */ onChange: PropTypes.func, diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index d575657e9..ffcc8e737 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -14,7 +14,7 @@ var NativeMethodsMixin = require('NativeMethodsMixin'); var React = require('React'); var POPAnimation = require('POPAnimation'); -var Animation = require('Animation'); +var AnimationExperimental = require('AnimationExperimental'); var Touchable = require('Touchable'); var merge = require('merge'); @@ -79,7 +79,7 @@ var TouchableBounce = React.createClass({ this.state.animationID = POPAnimation.createSpringAnimation(anim); this.addAnimation(this.state.animationID, callback); } else { - Animation.startAnimation(this, 300, 0, 'easeOutBack', {scaleXY: [value, value]}); + AnimationExperimental.startAnimation(this, 300, 0, 'easeOutBack', {scaleXY: [value, value]}); if (fromValue && typeof fromValue === 'function') { callback = fromValue; } diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js old mode 100644 new mode 100755 index 1b1178ebb..cd9ea02fd --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -13,7 +13,6 @@ var React = require('React'); var Touchable = require('Touchable'); - var onlyChild = require('onlyChild'); /** @@ -78,10 +77,8 @@ var TouchableWithoutFeedback = React.createClass({ }, render: function(): ReactElement { - // Note(vjeux): use cloneWithProps once React has been upgraded - var child = onlyChild(this.props.children); // Note(avik): remove dynamic typecast once Flow has been upgraded - return (React: any).cloneElement(child, { + return (React: any).cloneElement(onlyChild(this.props.children), { accessible: true, testID: this.props.testID, onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder, diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index e34c13883..c981e4129 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule View + * @flow */ 'use strict'; diff --git a/Libraries/CustomComponents/ListView/ListViewDataSource.js b/Libraries/CustomComponents/ListView/ListViewDataSource.js index 2f5759768..8e8000781 100644 --- a/Libraries/CustomComponents/ListView/ListViewDataSource.js +++ b/Libraries/CustomComponents/ListView/ListViewDataSource.js @@ -32,38 +32,6 @@ var invariant = require('invariant'); var isEmpty = require('isEmpty'); var warning = require('warning'); -/** - * ListViewDataSource - Provides efficient data processing and access to the - * ListView component. A ListViewDataSource is created with functions for - * extracting data from the input blob, and comparing elements (with default - * implementations for convenience). The input blob can be as simple as an - * array of strings, or an object with rows nested inside section objects. - * - * To update the data in the datasource, use `cloneWithRows` (or - * `cloneWithRowsAndSections` if you care about sections). The data in the - * data source is immutable, so you can't modify it directly. The clone methods - * suck in the new data and compute a diff for each row so ListView knows - * whether to re-render it or not. - * - * In this example, a component receives data in chunks, handled by - * `_onDataArrived`, which concats the new data onto the old data and updates the - * data source. We use `concat` to create a new array - mutating `this._data`, - * e.g. with `this._data.push(newRowData)`, would be an error. `_rowHasChanged` - * understands the shape of the row data and knows how to efficiently compare - * it. - * - * getInitialState: function() { - * var ds = new ListViewDataSource({rowHasChanged: this._rowHasChanged}); - * return {ds}; - * }, - * _onDataArrived(newData) { - * this._data = this._data.concat(newData); - * this.setState({ - * ds: this.state.ds.cloneWithRows(this._data) - * }); - * } - */ - function defaultGetRowData( dataBlob: any, sectionID: number | string, @@ -88,15 +56,58 @@ type ParamType = { getSectionHeaderData: ?typeof defaultGetSectionHeaderData; } +/** + * Provides efficient data processing and access to the + * `ListView` component. A `ListViewDataSource` is created with functions for + * extracting data from the input blob, and comparing elements (with default + * implementations for convenience). The input blob can be as simple as an + * array of strings, or an object with rows nested inside section objects. + * + * To update the data in the datasource, use `cloneWithRows` (or + * `cloneWithRowsAndSections` if you care about sections). The data in the + * data source is immutable, so you can't modify it directly. The clone methods + * suck in the new data and compute a diff for each row so ListView knows + * whether to re-render it or not. + * + * In this example, a component receives data in chunks, handled by + * `_onDataArrived`, which concats the new data onto the old data and updates the + * data source. We use `concat` to create a new array - mutating `this._data`, + * e.g. with `this._data.push(newRowData)`, would be an error. `_rowHasChanged` + * understands the shape of the row data and knows how to efficiently compare + * it. + * + * ``` + * getInitialState: function() { + * var ds = new ListViewDataSource({rowHasChanged: this._rowHasChanged}); + * return {ds}; + * }, + * _onDataArrived(newData) { + * this._data = this._data.concat(newData); + * this.setState({ + * ds: this.state.ds.cloneWithRows(this._data) + * }); + * } + * ``` + */ + class ListViewDataSource { /** - * @param {ParamType} params - * - * You can provide custom extraction and 'hasChanged' functions for section + * You can provide custom extraction and `hasChanged` functions for section * headers and rows. If absent, data will be extracted with the * `defaultGetRowData` and `defaultGetSectionHeaderData` functions. * + * The default extractor expects data of one of the following forms: + * + * { sectionID_1: { rowID_1: , ... }, ... } + * + * or + * + * [ [ , , ... ], ... ] + * + * The constructor takes in a params argument that can contain any of the + * following: + * * - getRowData(dataBlob, sectionID, rowID); * - getSectionHeaderData(dataBlob, sectionID); * - rowHasChanged(prevRowData, nextRowData); @@ -125,14 +136,25 @@ class ListViewDataSource { } /** - * @param {object} dataBlob -- This is an arbitrary blob of data. An extractor - * function was defined at construction time. The default extractor assumes - * the data is a plain array or keyed object. - */ - cloneWithRows( - dataBlob: Array | {[key: string]: any}, - rowIdentities: ?Array - ): ListViewDataSource { + * Clones this `ListViewDataSource` with the specified `dataBlob` and + * `rowIdentities`. The `dataBlob` is just an aribitrary blob of data. At + * construction an extractor to get the interesting informatoin was defined + * (or the default was used). + * + * The `rowIdentities` is is a 2D array of identifiers for rows. + * ie. [['a1', 'a2'], ['b1', 'b2', 'b3'], ...]. If not provided, it's + * assumed that the keys of the section data are the row identities. + * + * Note: This function does NOT clone the data in this data source. It simply + * passes the functions defined at construction to a new data source with + * the data specified. If you wish to maintain the existing data you must + * handle merging of old and new data separately and then pass that into + * this function as the `dataBlob`. + */ + cloneWithRows( + dataBlob: Array | {[key: string]: any}, + rowIdentities: ?Array + ): ListViewDataSource { var rowIds = rowIdentities ? [rowIdentities] : null; if (!this._sectionHeaderHasChanged) { this._sectionHeaderHasChanged = () => false; @@ -141,29 +163,20 @@ class ListViewDataSource { } /** - * @param {object} dataBlob -- This is an arbitrary blob of data. An extractor - * function was defined at construction time. The default extractor assumes - * the data is a nested array or keyed object of the form: + * This performs the same function as the `cloneWithRows` function but here + * you also specify what your `sectionIdentities` are. If you don't care + * about sections you should safely be able to use `cloneWithRows`. * - * { sectionID_1: { rowID_1: , ... }, ... } - * - * or - * - * [ [ , , ... ], ... ] - * - * @param {array} sectionIdentities -- This is an array of identifiers for - * sections. ie. ['s1', 's2', ...]. If not provided, it's assumed that the - * keys of dataBlob are the section identities. - * @param {array} rowIdentities -- This is a 2D array of identifiers for rows. - * ie. [['a1', 'a2'], ['b1', 'b2', 'b3'], ...]. If not provided, it's - * assumed that the keys of the section data are the row identities. + * `sectionIdentities` is an array of identifiers for sections. + * ie. ['s1', 's2', ...]. If not provided, it's assumed that the + * keys of dataBlob are the section identities. * * Note: this returns a new object! */ cloneWithRowsAndSections( - dataBlob: any, - sectionIdentities: ?Array, - rowIdentities: ?Array> + dataBlob: any, + sectionIdentities: ?Array, + rowIdentities: ?Array> ): ListViewDataSource { invariant( typeof this._sectionHeaderHasChanged === 'function', @@ -205,9 +218,6 @@ class ListViewDataSource { } /** - * @param {number} sectionIndex - * @param {number} rowIndex - * * Returns if the row is dirtied and needs to be rerendered */ rowShouldUpdate(sectionIndex: number, rowIndex: number): bool { @@ -218,9 +228,6 @@ class ListViewDataSource { } /** - * @param {number} sectionIndex - * @param {number} rowIndex - * * Gets the data required to render the row. */ getRowData(sectionIndex: number, rowIndex: number): any { @@ -234,8 +241,6 @@ class ListViewDataSource { } /** - * @param {number} index - * * Gets the rowID at index provided if the dataSource arrays were flattened, * or null of out of range indexes. */ @@ -252,8 +257,6 @@ class ListViewDataSource { } /** - * @param {number} index - * * Gets the sectionID at index provided if the dataSource arrays were flattened, * or null for out of range indexes. */ @@ -281,8 +284,6 @@ class ListViewDataSource { } /** - * @param {number} sectionIndex - * * Returns if the section header is dirtied and needs to be rerendered */ sectionHeaderShouldUpdate(sectionIndex: number): bool { @@ -293,8 +294,6 @@ class ListViewDataSource { } /** - * @param {number} sectionIndex - * * Gets the data required to render the section header */ getSectionHeaderData(sectionIndex: number): any { diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index acf9b383f..89f3b49c2 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -54,8 +54,6 @@ var SCREEN_HEIGHT = Dimensions.get('window').height; var OFF_SCREEN = {style: {opacity: 0}}; -var NAVIGATION_BAR_REF = 'navigationBar_ref'; - var __uid = 0; function getuid() { return __uid++; @@ -94,6 +92,12 @@ var styles = StyleSheet.create({ } }); +var GESTURE_ACTIONS = [ + 'pop', + 'jumpBack', + 'jumpForward', +]; + /** * Use `Navigator` to transition between different scenes in your app. To * accomplish this, provide route objects to the navigator to identify each @@ -314,6 +318,7 @@ var Navigator = React.createClass({ popToRoute: this.popToRoute, popToTop: this.popToTop, parentNavigator: this.props.navigator, + getCurrentRoutes: this.getCurrentRoutes, // We want to bubble focused routes to the top navigation stack. If we // are a child navigator, this allows us to call props.navigator.on*Focus // of the topmost Navigator @@ -457,15 +462,18 @@ var Navigator = React.createClass({ _completeTransition: function() { if (this.spring.getCurrentValue() === 1) { var presentedIndex = this.state.toIndex; - this.state.fromIndex = presentedIndex; this.state.presentedIndex = presentedIndex; + this.state.fromIndex = presentedIndex; this._emitDidFocus(presentedIndex); this._removePoppedRoutes(); if (AnimationsDebugModule) { AnimationsDebugModule.stopRecordingFps(Date.now()); } - this._hideOtherScenes(presentedIndex); + } else { + this.state.fromIndex = this.state.presentedIndex; + this.state.toIndex = this.state.presentedIndex; } + this._hideOtherScenes(presentedIndex); }, _transitionToToIndexWithVelocity: function(v) { @@ -499,6 +507,10 @@ var Navigator = React.createClass({ _emitWillFocus: function(index) { var route = this.state.routeStack[index]; + var navBar = this._navBar; + if (navBar && navBar.handleWillFocus) { + navBar.handleWillFocus(route); + } if (this.props.onWillFocus) { this.props.onWillFocus(route); } else if (this.props.navigator && this.props.navigator.onWillFocus) { @@ -532,95 +544,170 @@ var Navigator = React.createClass({ _handleMoveShouldSetPanResponder: function(e, gestureState) { var currentRoute = this.state.routeStack[this.state.presentedIndex]; - var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - if (!animationConfig.enableGestures) { - return false; - } - var currentLoc = animationConfig.isVertical ? gestureState.moveY : gestureState.moveX; - var travelDist = animationConfig.isVertical ? gestureState.dy : gestureState.dx; - var oppositeAxisTravelDist = - animationConfig.isVertical ? gestureState.dx : gestureState.dy; - var moveStartedInRegion = currentLoc < animationConfig.edgeHitWidth; - var moveTravelledFarEnough = - travelDist >= animationConfig.gestureDetectMovement && - travelDist > oppositeAxisTravelDist * animationConfig.directionRatio; - return ( - !this.state.isResponderOnlyToBlockTouches && - moveStartedInRegion && - !this.state.isAnimating && - this.state.presentedIndex > 0 && - moveTravelledFarEnough - ); + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; + this._expectingGestureGrant = this._matchGestureAction(sceneConfig.gestures, gestureState); + return !! this._expectingGestureGrant; + }, + + _doesGestureOverswipe: function(gestureName) { + var wouldOverswipeBack = this.state.presentedIndex <= 0 && + (gestureName === 'pop' || gestureName === 'jumpBack'); + var wouldOverswipeForward = this.state.presentedIndex >= this.state.routeStack.length - 1 && + gestureName === 'jumpForward'; + return wouldOverswipeForward || wouldOverswipeBack; }, _handlePanResponderGrant: function(e, gestureState) { + invariant( + this._expectingGestureGrant, + 'Responder granted unexpectedly.' + ); + this._activeGestureAction = this._expectingGestureGrant; + this._expectingGestureGrant = null; this.state.isResponderOnlyToBlockTouches = this.state.isAnimating; if (!this.state.isAnimating) { this.state.fromIndex = this.state.presentedIndex; - this.state.toIndex = this.state.presentedIndex - 1; + var gestureSceneDelta = this._deltaForGestureAction(this._activeGestureAction); + this.state.toIndex = this.state.presentedIndex + gestureSceneDelta; + } + }, + + _deltaForGestureAction: function(gestureAction) { + switch (gestureAction) { + case 'pop': + case 'jumpBack': + return -1; + case 'jumpForward': + return 1; + default: + invariant(false, 'Unsupported gesture action ' + gestureAction); + return; } }, _handlePanResponderRelease: function(e, gestureState) { + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; + var releaseGestureAction = this._activeGestureAction; + this._activeGestureAction = null; if (this.state.isResponderOnlyToBlockTouches) { this.state.isResponderOnlyToBlockTouches = false; return; } - var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - var velocity = animationConfig.isVertical ? gestureState.vy : gestureState.vx; - // It's not the real location. There is no *real* location - that's the - // point of the pan gesture. - var pseudoLocation = animationConfig.isVertical ? - gestureState.y0 + gestureState.dy : - gestureState.x0 + gestureState.dx; - var still = Math.abs(velocity) < animationConfig.notMoving; + var releaseGesture = sceneConfig.gestures[releaseGestureAction]; if (this.spring.getCurrentValue() === 0) { + // The spring is at zero, so the gesture is already complete this.spring.setCurrentValue(0).setAtRest(); this._completeTransition(); return; } - var transitionVelocity = - still && animationConfig.pastPointOfNoReturn(pseudoLocation) ? animationConfig.snapVelocity : - still && !animationConfig.pastPointOfNoReturn(pseudoLocation) ? -animationConfig.snapVelocity : - clamp(-10, velocity, 10); // What are Rebound UoM? - + var isTravelVertical = releaseGesture.direction === 'top-to-bottom' || releaseGesture.direction === 'bottom-to-top'; + var isTravelInverted = releaseGesture.direction === 'right-to-left' || releaseGesture.direction === 'bottom-to-top'; + var velocity, gestureDistance; + if (isTravelVertical) { + velocity = isTravelInverted ? -gestureState.vy : gestureState.vy; + gestureDistance = isTravelInverted ? -gestureState.dy : gestureState.dy; + } else { + velocity = isTravelInverted ? -gestureState.vx : gestureState.vx; + gestureDistance = isTravelInverted ? -gestureState.dx : gestureState.dx; + } + var transitionVelocity = clamp(-10, velocity, 10); + if (Math.abs(velocity) < releaseGesture.notMoving) { + // The gesture velocity is so slow, is "not moving" + var hasGesturedEnoughToComplete = gestureDistance > releaseGesture.fullDistance * releaseGesture.stillCompletionRatio; + transitionVelocity = hasGesturedEnoughToComplete ? releaseGesture.snapVelocity : -releaseGesture.snapVelocity; + } this.spring.setOvershootClampingEnabled(true); - if (transitionVelocity < 0) { + if (transitionVelocity < 0 || this._doesGestureOverswipe(releaseGestureAction)) { this._transitionToFromIndexWithVelocity(transitionVelocity); } else { - this._manuallyPopBackstack(1); this._transitionToToIndexWithVelocity(transitionVelocity); } }, _handlePanResponderTerminate: function(e, gestureState) { + this._activeGestureAction = null; this.state.isResponderOnlyToBlockTouches = false; this._transitionToFromIndexWithVelocity(0); }, _handlePanResponderMove: function(e, gestureState) { if (!this.state.isResponderOnlyToBlockTouches) { - var animationConfig = this.state.sceneConfigStack[this.state.presentedIndex]; - var distance = animationConfig.isVertical ? gestureState.dy : gestureState.dx; - var gestureDetectMovement = animationConfig.gestureDetectMovement; + var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex]; + var gesture = sceneConfig.gestures[this._activeGestureAction]; + var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top'; + var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top'; + var distance = isTravelVertical ? gestureState.dy : gestureState.dx; + distance = isTravelInverted ? - distance : distance; + var gestureDetectMovement = gesture.gestureDetectMovement; var nextProgress = (distance - gestureDetectMovement) / - (animationConfig.screenDimension - gestureDetectMovement); + (gesture.fullDistance - gestureDetectMovement); + if (this._doesGestureOverswipe(this._activeGestureAction)) { + var frictionConstant = gesture.overswipe.frictionConstant; + var frictionByDistance = gesture.overswipe.frictionByDistance; + var frictionRatio = 1 / ((frictionConstant) + (Math.abs(nextProgress) * frictionByDistance)); + nextProgress *= frictionRatio; + } this.spring.setCurrentValue(clamp(0, nextProgress, 1)); } }, + _matchGestureAction: function(gestures, gestureState) { + if (!gestures) { + return null; + } + if (this.state.isResponderOnlyToBlockTouches || this.state.isAnimating) { + return null; + } + var matchedGesture = null; + GESTURE_ACTIONS.some((gestureName) => { + var gesture = gestures[gestureName]; + if (!gesture) { + return; + } + if (gesture.overswipe == null && this._doesGestureOverswipe(gestureName)) { + // cannot swipe past first or last scene without overswiping + return false; + } + var isTravelVertical = gesture.direction === 'top-to-bottom' || gesture.direction === 'bottom-to-top'; + var isTravelInverted = gesture.direction === 'right-to-left' || gesture.direction === 'bottom-to-top'; + var currentLoc = isTravelVertical ? gestureState.moveY : gestureState.moveX; + var travelDist = isTravelVertical ? gestureState.dy : gestureState.dx; + var oppositeAxisTravelDist = + isTravelVertical ? gestureState.dx : gestureState.dy; + if (isTravelInverted) { + currentLoc = -currentLoc; + travelDist = -travelDist; + oppositeAxisTravelDist = -oppositeAxisTravelDist; + } + var moveStartedInRegion = gesture.edgeHitWidth == null || + currentLoc < gesture.edgeHitWidth; + var moveTravelledFarEnough = + travelDist >= gesture.gestureDetectMovement && + travelDist > oppositeAxisTravelDist * gesture.directionRatio; + if (moveStartedInRegion && moveTravelledFarEnough) { + matchedGesture = gestureName; + return true; + } + }); + return matchedGesture; + }, + _transitionSceneStyle: function(fromIndex, toIndex, progress, index) { var viewAtIndex = this.refs['scene_' + index]; if (viewAtIndex === null || viewAtIndex === undefined) { return; } // Use toIndex animation when we move forwards. Use fromIndex when we move back - var animationIndex = this.state.presentedIndex < toIndex ? toIndex : fromIndex; - var animationConfig = this.state.sceneConfigStack[animationIndex]; + var sceneConfigIndex = this.state.presentedIndex < toIndex ? toIndex : fromIndex; + var sceneConfig = this.state.sceneConfigStack[sceneConfigIndex]; + // this happens for overswiping when there is no scene at toIndex + if (!sceneConfig) { + sceneConfig = this.state.sceneConfigStack[sceneConfigIndex - 1]; + } var styleToUse = {}; var useFn = index < fromIndex || index < toIndex ? - animationConfig.interpolators.out : - animationConfig.interpolators.into; + sceneConfig.animationInterpolators.out : + sceneConfig.animationInterpolators.into; var directionAdjustedProgress = fromIndex < toIndex ? progress : 1 - progress; var didChange = useFn(styleToUse, directionAdjustedProgress); if (didChange) { @@ -631,7 +718,7 @@ var Navigator = React.createClass({ _transitionBetween: function(fromIndex, toIndex, progress) { this._transitionSceneStyle(fromIndex, toIndex, progress, fromIndex); this._transitionSceneStyle(fromIndex, toIndex, progress, toIndex); - var navBar = this.refs[NAVIGATION_BAR_REF]; + var navBar = this._navBar; if (navBar && navBar.updateProgress) { navBar.updateProgress(progress, fromIndex, toIndex); } @@ -912,6 +999,10 @@ var Navigator = React.createClass({ } }, + getCurrentRoutes: function() { + return this.state.routeStack; + }, + _onItemRef: function(itemId, ref) { this._itemRefs[itemId] = ref; var itemIndex = this.state.idStack.indexOf(itemId); @@ -989,7 +1080,7 @@ var Navigator = React.createClass({ return null; } return React.cloneElement(this.props.navigationBar, { - ref: NAVIGATION_BAR_REF, + ref: (navBar) => { this._navBar = navBar; }, navigator: this.navigatorActions, navState: this.state, }); diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index 89e5de9e1..3072074b2 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -30,7 +30,6 @@ var Dimensions = require('Dimensions'); var PixelRatio = require('PixelRatio'); var buildStyleInterpolator = require('buildStyleInterpolator'); -var merge = require('merge'); var SCREEN_WIDTH = Dimensions.get('window').width; var SCREEN_HEIGHT = Dimensions.get('window').height; @@ -220,32 +219,12 @@ var FromTheFront = { }, }; - -var Interpolators = { - Vertical: { - into: buildStyleInterpolator(FromTheFront), - out: buildStyleInterpolator(ToTheBack), - }, - Horizontal: { - into: buildStyleInterpolator(FromTheRight), - out: buildStyleInterpolator(ToTheLeft), - }, +var BaseOverswipeConfig = { + frictionConstant: 1, + frictionByDistance: 1.5, }; - -// These are meant to mimic iOS default behavior -var PastPointOfNoReturn = { - horizontal: function(location) { - return location > SCREEN_WIDTH * 3 / 5; - }, - vertical: function(location) { - return location > SCREEN_HEIGHT * 3 / 5; - }, -}; - -var BaseConfig = { - // When false, all gestures are ignored for this scene - enableGestures: true, +var BaseLeftToRightGesture = { // How far the swipe must drag to start transitioning gestureDetectMovement: 2, @@ -253,48 +232,88 @@ var BaseConfig = { // Amplitude of release velocity that is considered still notMoving: 0.3, - // Velocity to start at when transitioning without gesture - defaultTransitionVelocity: 1.5, - // Fraction of directional move required. directionRatio: 0.66, // Velocity to transition with when the gesture release was "not moving" snapVelocity: 2, + // Region that can trigger swipe. iOS default is 30px from the left edge + edgeHitWidth: 30, + + // Ratio of gesture completion when non-velocity release will cause action + stillCompletionRatio: 3 / 5, + + fullDistance: SCREEN_WIDTH, + + direction: 'left-to-right', + +}; + +var BaseRightToLeftGesture = { + ...BaseLeftToRightGesture, + direction: 'right-to-left', +}; + +var BaseConfig = { + // A list of all gestures that are enabled on this scene + gestures: { + pop: BaseLeftToRightGesture, + }, + // Rebound spring parameters when transitioning FROM this scene springFriction: 26, springTension: 200, - // Defaults for horizontal transitioning: + // Velocity to start at when transitioning without gesture + defaultTransitionVelocity: 1.5, - isVertical: false, - screenDimension: SCREEN_WIDTH, - - // Region that can trigger swipe. iOS default is 30px from the left edge - edgeHitWidth: 30, - - // Point at which a non-velocity release will cause nav pop - pastPointOfNoReturn: PastPointOfNoReturn.horizontal, - - // Animation interpolators for this transition - interpolators: Interpolators.Horizontal, + // Animation interpolators for horizontal transitioning: + animationInterpolators: { + into: buildStyleInterpolator(FromTheRight), + out: buildStyleInterpolator(ToTheLeft), + }, }; var NavigatorSceneConfigs = { - PushFromRight: merge(BaseConfig, { + PushFromRight: { + ...BaseConfig, // We will want to customize this soon - }), - FloatFromRight: merge(BaseConfig, { + }, + FloatFromRight: { + ...BaseConfig, // We will want to customize this soon - }), - FloatFromBottom: merge(BaseConfig, { - edgeHitWidth: 150, - interpolators: Interpolators.Vertical, - isVertical: true, - pastPointOfNoReturn: PastPointOfNoReturn.vertical, - screenDimension: SCREEN_HEIGHT, - }), + }, + FloatFromBottom: { + ...BaseConfig, + gestures: { + pop: { + ...BaseLeftToRightGesture, + edgeHitWidth: 150, + direction: 'top-to-bottom', + fullDistance: SCREEN_HEIGHT, + } + }, + animationInterpolators: { + into: buildStyleInterpolator(FromTheFront), + out: buildStyleInterpolator(ToTheBack), + }, + }, + HorizontalSwipeJump: { + ...BaseConfig, + gestures: { + jumpBack: { + ...BaseLeftToRightGesture, + overswipe: BaseOverswipeConfig, + edgeHitWidth: null, + }, + jumpForward: { + ...BaseRightToLeftGesture, + overswipe: BaseOverswipeConfig, + edgeHitWidth: null, + }, + } + } }; module.exports = NavigatorSceneConfigs; diff --git a/Libraries/Portal/Portal.js b/Libraries/Portal/Portal.js deleted file mode 100644 index 762af3661..000000000 --- a/Libraries/Portal/Portal.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2004-present Facebook. All Rights Reserved. - * - * @providesModule Portal - * @flow - */ -'use strict'; - -var React = require('React'); -var StyleSheet = require('StyleSheet'); -var View = require('View'); - -var _portalRef: any; - -/* - * A container that renders all the modals on top of everything else in the application. - * - * Portal makes it possible for application code to pass modal views all the way up to - * the root element created in `renderApplication`. - * - * Never use `` in your code. There is only one Portal instance rendered - * by the top-level `renderApplication`. - */ -var Portal = React.createClass({ - statics: { - showModal: function(component) { - if (!_portalRef) { - console.error('Calling showModal but no Portal has been rendered'); - return; - } - _portalRef.setState({modal: component}); - }, - - closeModal: function() { - if (!_portalRef) { - console.error('Calling closeModal but no Portal has been rendered'); - return; - } - _portalRef.setState({modal: null}); - }, - }, - - getInitialState: function() { - return {modal: (null: any)}; - }, - - render: function() { - _portalRef = this; - if (!this.state.modal) { - return ; - } - return ( - - {this.state.modal} - - ); - } -}); - -var styles = StyleSheet.create({ - modalsContainer: { - position: 'absolute', - left: 0, - top: 0, - right: 0, - bottom: 0, - }, -}); - -module.exports = Portal; diff --git a/Libraries/ReactIOS/renderApplication.js b/Libraries/ReactIOS/renderApplication.ios.js similarity index 59% rename from Libraries/ReactIOS/renderApplication.js rename to Libraries/ReactIOS/renderApplication.ios.js index dbec1e853..084390ac5 100644 --- a/Libraries/ReactIOS/renderApplication.js +++ b/Libraries/ReactIOS/renderApplication.ios.js @@ -11,10 +11,7 @@ */ 'use strict'; -var Portal = require('Portal'); var React = require('React'); -var StyleSheet = require('StyleSheet'); -var View = require('View'); var invariant = require('invariant'); @@ -28,26 +25,11 @@ function renderApplication( 'Expect to have a valid rootTag, instead got ', rootTag ); React.render( - - - - , + , rootTag ); } -var styles = StyleSheet.create({ - // This is needed so the application covers the whole screen - // and therefore the contents of the Portal are not clipped. - appContainer: { - position: 'absolute', - left: 0, - top: 0, - right: 0, - bottom: 0, - }, -}); - module.exports = renderApplication; diff --git a/Libraries/Utilities/AlertIOS.js b/Libraries/Utilities/AlertIOS.js index f9a8df60d..de72d7b9f 100644 --- a/Libraries/Utilities/AlertIOS.js +++ b/Libraries/Utilities/AlertIOS.js @@ -20,32 +20,28 @@ var DEFAULT_BUTTON = { }; /** - * AlertIOS manages native iOS alerts, option sheets, and share dialogs + * Launches an alert dialog with the specified title and message. + * + * Optionally provide a list of buttons. Tapping any button will fire the + * respective onPress callback and dismiss the alert. By default, the only + * button will be an 'OK' button + * + * The last button in the list will be considered the 'Primary' button and + * it will appear bold. + * + * ``` + * AlertIOS.alert( + * 'Foo Title', + * 'My Alert Msg', + * [ + * {text: 'Foo', onPress: () => console.log('Foo Pressed!')}, + * {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, + * ] + * )} + * ``` */ class AlertIOS { - - /** - * Launches an alert dialog with the specified title and message. - * - * Optionally provide a list of buttons. Tapping any button will fire the - * respective onPress callback and dismiss the alert. By default, the only - * button will be an 'OK' button - * - * The last button in the list will be considered the 'Primary' button and - * it will appear bold. - * - * ``` - * AlertIOS.alert( - * 'Foo Title', - * 'My Alert Msg', - * [ - * {text: 'Foo', onPress: () => console.log('Foo Pressed!')}, - * {text: 'Bar', onPress: () => console.log('Bar Pressed!')}, - * ] - * )} - * ``` - */ static alert( title: ?string, message?: ?string, diff --git a/Libraries/react-native/react-native.js b/Libraries/react-native/react-native.js index 46148bd86..1aa978f46 100644 --- a/Libraries/react-native/react-native.js +++ b/Libraries/react-native/react-native.js @@ -41,7 +41,6 @@ var ReactNative = Object.assign(Object.create(require('React')), { // APIs AlertIOS: require('AlertIOS'), - Animation: require('Animation'), AppRegistry: require('AppRegistry'), AppStateIOS: require('AppStateIOS'), AsyncStorage: require('AsyncStorage'), diff --git a/React.podspec b/React.podspec index 8abc2a5ba..65dc93eb0 100644 --- a/React.podspec +++ b/React.podspec @@ -44,9 +44,9 @@ Pod::Spec.new do |s| ss.preserve_paths = "Libraries/AdSupport/*.js" end - s.subspec 'RCTAnimation' do |ss| + s.subspec 'RCTAnimationExperimental' do |ss| ss.dependency 'React/Core' - ss.source_files = "Libraries/Animation/*.{h,m}" + ss.source_files = "Libraries/Animation/RCTAnimationExperimental*.{h,m}" ss.preserve_paths = "Libraries/Animation/*.js" end diff --git a/cli.js b/cli.js index 7a36c4c6e..2c527a5df 100644 --- a/cli.js +++ b/cli.js @@ -1,4 +1,3 @@ - /** * Copyright 2004-present Facebook. All Rights Reserved. */ diff --git a/docs/Image.md b/docs/Image.md index 2ccc915d1..05b675197 100644 --- a/docs/Image.md +++ b/docs/Image.md @@ -1,4 +1,4 @@ -Displaying images is a fascinating subject, React Native uses some cool tricks to make it a better experience. +Displaying images is a fascinating subject; React Native uses some cool tricks to make it a better experience. ## No Automatic Sizing diff --git a/docs/NativeModulesIOS.md b/docs/NativeModulesIOS.md index 6b204cdb3..f6637a2cd 100644 --- a/docs/NativeModulesIOS.md +++ b/docs/NativeModulesIOS.md @@ -15,7 +15,7 @@ This is a more advanced guide that shows how to build a native module. It assume ## iOS Calendar module example -This guide will use [iOS Calendar API](https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/EventKitProgGuide/Introduction/Introduction.html) example. Let's say we would like to be able to access iOS calendar from JavaScript. +This guide will use [iOS Calendar API](https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/EventKitProgGuide/Introduction/Introduction.html) example. Let's say we would like to be able to access the iOS calendar from JavaScript. Native module is just an Objectve-C class that implements `RCTBridgeModule` protocol. If you are wondering, RCT is a shorthand for ReaCT. @@ -52,7 +52,7 @@ CalendarManager.addEventWithName('Birthday Party', '4 Privet Drive, Surrey'); Notice that the exported method name was generated from first part of Objective-C selector. Sometimes it results in a non-idiomatic JavaScript name (like the one in our example). You can change the name by supplying an optional argument to `RCT_EXPORT`, e.g. `RCT_EXPORT(addEvent)`. -The return type of the method should always be `void`. React Native bridge is asynchronous, so the only way to pass result to JavaScript is by using callbacks or emitting events (see below). +The return type of the method should always be `void`. React Native bridge is asynchronous, so the only way to pass a result to JavaScript is by using callbacks or emitting events (see below). ## Argument types @@ -109,7 +109,7 @@ CalendarManager.addEvent('Birthday Party', { > > This section is even more experimental than others, we don't have a set of best practices around callbacks yet. -Native module also supports a special kind of argument - callback. In most cases it is used to provide function call result to JavaScript. +Native module also supports a special kind of argument- a callback. In most cases it is used to provide the function call result to JavaScript. ```objective-c - (void)findEvents:(RCTResponseSenderBlock)callback @@ -120,7 +120,7 @@ Native module also supports a special kind of argument - callback. In most cases } ``` -`RCTResponseSenderBlock` accepts only one argument - array of arguments to pass to JavaScript callback. In this case we use node's convention to set first argument to error and the rest - to the result of the function. +`RCTResponseSenderBlock` accepts only one argument - an array of arguments to pass to the JavaScript callback. In this case we use node's convention to set first argument to error and the rest - to the result of the function. ```javascript CalendarManager.findEvents((error, events) => { @@ -132,7 +132,7 @@ CalendarManager.findEvents((error, events) => { }) ``` -Native module is supposed to invoke callback only once. It can, however, store the callback as an ivar and invoke it later. This pattern is often used to wrap iOS APIs that require delegate. See [`RCTAlertManager`](https://github.com/facebook/react-native/blob/master/React/Modules/RCTAlertManager.m). +Native module is supposed to invoke its callback only once. It can, however, store the callback as an ivar and invoke it later. This pattern is often used to wrap iOS APIs that require delegate. See [`RCTAlertManager`](https://github.com/facebook/react-native/blob/master/React/Modules/RCTAlertManager.m). If you want to pass error-like object to JavaScript, use `RCTMakeError` from [`RCTUtils.h`](https://github.com/facebook/react-native/blob/master/React/Base/RCTUtils.h). diff --git a/docs/Network.md b/docs/Network.md index da7c7a6d4..5b26f51f9 100644 --- a/docs/Network.md +++ b/docs/Network.md @@ -7,7 +7,7 @@ permalink: docs/network.html next: timers --- -One of React Native's goals is to be a playground where we can experiment with different architectures and crazy ideas. Since browsers are not flexible enough, we had no choice but to reimplement the entire stack. In the places that we did not intend to change, we tried to be as faithful as possible to the browser APIs. The networking stack is a great example. +One of React Native's goals is to be a playground where we can experiment with different architectures and crazy ideas. Since browsers are not flexible enough, we had no choice but to reimplement the entire stack. In the places that we did not intend to change anything, we tried to be as faithful as possible to the browser APIs. The networking stack is a great example. ## XMLHttpRequest diff --git a/docs/Testing.md b/docs/Testing.md index 5cb986fcf..5fbc88693 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -15,7 +15,7 @@ The React Native repo has several tests you can run to verify you haven't caused [Jest](http://facebook.github.io/jest/) tests are JS-only tests run on the command line with node. The tests themselves live in the `__tests__` directories of the files they test, and there is a large emphasis on aggressively mocking out functionality that is not under test for failure isolation and maximum speed. You can run the existing React Native jest tests with `npm test` from the react-native root, and we encourage you to add your own tests for any components you want to contribute to. See [`getImageSource-test.js`](https://github.com/facebook/react-native/blob/master/Examples/Movies/__tests__/getImageSource-test.js) for a basic example. -## Integration Tests. +## Integration Tests React Native provides facilities to make it easier to test integrated components that require both native and JS components to communicate across the bridge. The two main components are `RCTTestRunner` and `RCTTestModule`. `RCTTestRunner` sets up the ReactNative environment and provides facilities to run the tests as `XCTestCase`s in Xcode (`runTest:module` is the simplest method). `RCTTestModule` is exported to JS via `NativeModules` as `TestModule`. The tests themselves are written in JS, and must call `TestModule.markTestCompleted()` when they are done, otherwise the test will timeout and fail. Test failures are primarily indicated by throwing an exception. It is also possible to test error conditions with `runTest:module:initialProps:expectErrorRegex:` or `runTest:module:initialProps:expectErrorBlock:` which will expect an error to be thrown and verify the error matches the provided criteria. See [`IntegrationTestHarnessTest.js`](https://github.com/facebook/react-native/blob/master/IntegrationTests/IntegrationTestHarnessTest.js) and [`IntegrationTestsTests.m`](https://github.com/facebook/react-native/blob/master/IntegrationTests/IntegrationTestsTests/IntegrationTestsTests.m) for example usage. diff --git a/docs/Troubleshooting.md b/docs/Troubleshooting.md index c1b278b0a..8f7332f5b 100644 --- a/docs/Troubleshooting.md +++ b/docs/Troubleshooting.md @@ -44,7 +44,7 @@ Edit `AppDelegate.m` to use a different port. ## Watchman took too long to load -Permission settings prevent Watchman from loading. A recent update solves this, get a HEAD install of Watchman if you are experiening this error. +Permission settings prevent Watchman from loading. A recent update solves this, get a HEAD install of Watchman if you are experiencing this error. ``` brew uninstall watchman diff --git a/local-cli/cli.js b/local-cli/cli.js index 5319ceb75..f2e9ec6ce 100644 --- a/local-cli/cli.js +++ b/local-cli/cli.js @@ -47,6 +47,10 @@ function init(root, projectName) { spawn(path.resolve(__dirname, 'init.sh'), [projectName], {stdio:'inherit'}); } +if (require.main === module) { + run(); +} + module.exports = { run: run, init: init, diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js index 5f6ec8822..b6a978c63 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/__tests__/DependencyGraph-test.js @@ -13,7 +13,7 @@ jest .dontMock('path') .dontMock('absolute-path') .dontMock('../docblock') - .dontMock('../../requirePattern') + .dontMock('../../replacePatterns') .setMock('../../../ModuleDescriptor', function(data) {return data;}); describe('DependencyGraph', function() { @@ -64,7 +64,7 @@ describe('DependencyGraph', function() { }); }); - pit('should get dependencies', function() { + pit('should get dependencies with deprecated assets', function() { var root = '/root'; fs.__setMockFilesystem({ 'root': { @@ -83,7 +83,7 @@ describe('DependencyGraph', function() { var dgraph = new DependencyGraph({ roots: [root], fileWatcher: fileWatcher, - assetRoots: ['/root/imgs'] + assetRoots_DEPRECATED: ['/root/imgs'], }); return dgraph.load().then(function() { expect(dgraph.getOrderedDependencies('/root/index.js')) @@ -98,6 +98,97 @@ describe('DependencyGraph', function() { }); }); + pit('should get dependencies with relative assets', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("./imgs/a.png")' + ].join('\n'), + 'imgs': { + 'a.png': '' + }, + 'package.json': JSON.stringify({ + name: 'rootPackage' + }), + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher, + }); + return dgraph.load().then(function() { + expect(dgraph.getOrderedDependencies('/root/index.js')) + .toEqual([ + { + id: 'index', + altId: 'rootPackage/index', + path: '/root/index.js', + dependencies: ['./imgs/a.png'] + }, + { id: 'rootPackage/imgs/a.png', + path: '/root/imgs/a.png', + dependencies: [], + isAsset: true + }, + ]); + }); + }); + + pit('Deprecated and relative assets can live together', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("./imgs/a.png")', + 'require("image!a")', + ].join('\n'), + 'imgs': { + 'a.png': '' + }, + 'package.json': JSON.stringify({ + name: 'rootPackage' + }), + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher, + assetRoots_DEPRECATED: ['/root/imgs'], + }); + return dgraph.load().then(function() { + expect(dgraph.getOrderedDependencies('/root/index.js')) + .toEqual([ + { + id: 'index', + altId: 'rootPackage/index', + path: '/root/index.js', + dependencies: ['./imgs/a.png', 'image!a'] + }, + { + id: 'rootPackage/imgs/a.png', + path: '/root/imgs/a.png', + dependencies: [], + isAsset: true + }, + { + id: 'image!a', + path: '/root/imgs/a.png', + dependencies: [], + isAsset: true + }, + ]); + }); + }); + pit('should get recursive dependencies', function() { var root = '/root'; fs.__setMockFilesystem({ @@ -821,7 +912,7 @@ describe('DependencyGraph', function() { }); }); - pit('updates module dependencies on asset add', function() { + pit('updates module dependencies on deprecated asset add', function() { var root = '/root'; var filesystem = fs.__setMockFilesystem({ 'root': { @@ -836,7 +927,7 @@ describe('DependencyGraph', function() { var dgraph = new DependencyGraph({ roots: [root], - assetRoots: [root], + assetRoots_DEPRECATED: [root], assetExts: ['png'], fileWatcher: fileWatcher }); @@ -870,6 +961,57 @@ describe('DependencyGraph', function() { }); }); + pit('updates module dependencies on relative asset add', function() { + var root = '/root'; + var filesystem = fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("./foo.png")' + ].join('\n'), + 'package.json': JSON.stringify({ + name: 'aPackage' + }), + }, + }); + + var dgraph = new DependencyGraph({ + roots: [root], + assetExts: ['png'], + fileWatcher: fileWatcher + }); + + return dgraph.load().then(function() { + expect(dgraph.getOrderedDependencies('/root/index.js')) + .toEqual([ + { id: 'index', altId: 'aPackage/index', + path: '/root/index.js', + dependencies: ['./foo.png'] + } + ]); + + filesystem.root['foo.png'] = ''; + triggerFileChange('add', 'foo.png', root); + + return dgraph.load().then(function() { + expect(dgraph.getOrderedDependencies('/root/index.js')) + .toEqual([ + { id: 'index', altId: 'aPackage/index', + path: '/root/index.js', + dependencies: ['./foo.png'] + }, + { id: 'aPackage/foo.png', + path: '/root/foo.png', + dependencies: [], + isAsset: true, + }, + ]); + }); + }); + }); + pit('runs changes through ignore filter', function() { var root = '/root'; var filesystem = fs.__setMockFilesystem({ diff --git a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js index 9ca430c49..3348907f1 100644 --- a/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/DependencyGraph/index.js @@ -12,7 +12,7 @@ var ModuleDescriptor = require('../../ModuleDescriptor'); var Promise = require('bluebird'); var fs = require('fs'); var docblock = require('./docblock'); -var requirePattern = require('../requirePattern'); +var replacePatterns = require('../replacePatterns'); var path = require('path'); var isAbsolutePath = require('absolute-path'); var debug = require('debug')('DependecyGraph'); @@ -37,7 +37,7 @@ var validateOpts = declareOpts({ type: 'object', required: true, }, - assetRoots: { + assetRoots_DEPRECATED: { type: 'array', default: [], }, @@ -51,7 +51,7 @@ function DependecyGraph(options) { var opts = validateOpts(options); this._roots = opts.roots; - this._assetRoots = opts.assetRoots; + this._assetRoots_DEPRECATED = opts.assetRoots_DEPRECATED; this._assetExts = opts.assetExts; this._ignoreFilePath = opts.ignoreFilePath; this._fileWatcher = options.fileWatcher; @@ -64,6 +64,10 @@ function DependecyGraph(options) { this._moduleById = Object.create(null); this._debugUpdateEvents = []; + this._moduleExtPattern = new RegExp( + '.' + ['js'].concat(this._assetExts).join('|') + '$' + ); + // Kick off the search process to precompute the dependency graph. this._init(); } @@ -75,7 +79,7 @@ DependecyGraph.prototype.load = function() { this._loading = Promise.all([ this._search(), - this._buildAssetMap(), + this._buildAssetMap_DEPRECATED(), ]); return this._loading; @@ -147,15 +151,15 @@ DependecyGraph.prototype.resolveDependency = function( fromModule, depModuleId ) { - if (this._assetMap != null) { - // Process asset requires. + if (this._assetMap_DEPRECATED != null) { var assetMatch = depModuleId.match(/^image!(.+)/); + // Process DEPRECATED global asset requires. if (assetMatch && assetMatch[1]) { - if (!this._assetMap[assetMatch[1]]) { + if (!this._assetMap_DEPRECATED[assetMatch[1]]) { debug('WARINING: Cannot find asset:', assetMatch[1]); return null; } - return this._assetMap[assetMatch[1]]; + return this._assetMap_DEPRECATED[assetMatch[1]]; } } @@ -218,7 +222,11 @@ DependecyGraph.prototype.resolveDependency = function( // fromModule.path: /x/y/z // modulePath: /x/y/a/b var dir = path.dirname(fromModule.path); - modulePath = withExtJs(path.join(dir, depModuleId)); + modulePath = path.join(dir, depModuleId); + + if (this._assetExts.indexOf(extname(modulePath)) === -1) { + modulePath = withExtJs(modulePath); + } dep = this._graph[modulePath]; @@ -287,7 +295,7 @@ DependecyGraph.prototype._search = function() { return false; } - return filePath.match(/\.js$/); + return filePath.match(self._moduleExtPattern); }); var processing = self._findAndProcessPackage(files, dir) @@ -370,11 +378,21 @@ DependecyGraph.prototype._removePackageFromIndices = function(packageJson) { * Parse a module and update indices. */ DependecyGraph.prototype._processModule = function(modulePath) { + var moduleData = { path: path.resolve(modulePath) }; + var module; + + if (this._assetExts.indexOf(extname(modulePath)) > -1) { + moduleData.id = this._lookupName(modulePath); + moduleData.isAsset = true; + moduleData.dependencies = []; + module = Promise.resolve(new ModuleDescriptor(moduleData)); + this._updateGraphWithModule(module); + } + var self = this; return readFile(modulePath, 'utf8') .then(function(content) { var moduleDocBlock = docblock.parseAsObject(content); - var moduleData = { path: path.resolve(modulePath) }; if (moduleDocBlock.providesModule || moduleDocBlock.provides) { moduleData.id = moduleDocBlock.providesModule || moduleDocBlock.provides; @@ -387,7 +405,7 @@ DependecyGraph.prototype._processModule = function(modulePath) { } moduleData.dependencies = extractRequires(content); - var module = new ModuleDescriptor(moduleData); + module = new ModuleDescriptor(moduleData); self._updateGraphWithModule(module); return module; }); @@ -497,8 +515,8 @@ DependecyGraph.prototype._processFileChange = function( this._debugUpdateEvents.push({event: eventType, path: filePath}); if (this._assetExts.indexOf(extname(filePath)) > -1) { - this._processAssetChange(eventType, absPath); - return; + this._processAssetChange_DEPRECATED(eventType, absPath); + // Fall through because new-style assets are actually modules. } var isPackage = path.basename(filePath) === 'package.json'; @@ -554,27 +572,28 @@ DependecyGraph.prototype._getAbsolutePath = function(filePath) { return null; }; -DependecyGraph.prototype._buildAssetMap = function() { - if (this._assetRoots == null || this._assetRoots.length === 0) { +DependecyGraph.prototype._buildAssetMap_DEPRECATED = function() { + if (this._assetRoots_DEPRECATED == null || + this._assetRoots_DEPRECATED.length === 0) { return Promise.resolve(); } - this._assetMap = Object.create(null); - return buildAssetMap( - this._assetRoots, - this._processAsset.bind(this) + this._assetMap_DEPRECATED = Object.create(null); + return buildAssetMap_DEPRECATED( + this._assetRoots_DEPRECATED, + this._processAsset_DEPRECATED.bind(this) ); }; -DependecyGraph.prototype._processAsset = function(file) { +DependecyGraph.prototype._processAsset_DEPRECATED = function(file) { var ext = extname(file); if (this._assetExts.indexOf(ext) !== -1) { var name = assetName(file, ext); - if (this._assetMap[name] != null) { + if (this._assetMap_DEPRECATED[name] != null) { debug('Conflcting assets', name); } - this._assetMap[name] = new ModuleDescriptor({ + this._assetMap_DEPRECATED[name] = new ModuleDescriptor({ id: 'image!' + name, path: path.resolve(file), isAsset: true, @@ -583,18 +602,18 @@ DependecyGraph.prototype._processAsset = function(file) { } }; -DependecyGraph.prototype._processAssetChange = function(eventType, file) { - if (this._assetMap == null) { +DependecyGraph.prototype._processAssetChange_DEPRECATED = function(eventType, file) { + if (this._assetMap_DEPRECATED == null) { return; } var name = assetName(file, extname(file)); if (eventType === 'change' || eventType === 'delete') { - delete this._assetMap[name]; + delete this._assetMap_DEPRECATED[name]; } if (eventType === 'change' || eventType === 'add') { - this._processAsset(file); + this._processAsset_DEPRECATED(file); } }; @@ -609,7 +628,11 @@ function extractRequires(code) { code .replace(blockCommentRe, '') .replace(lineCommentRe, '') - .replace(requirePattern, function(match, _, dep) { + .replace(replacePatterns.IMPORT_RE, function(match, pre, quot, dep, post) { + deps.push(dep); + return match; + }) + .replace(replacePatterns.REQUIRE_RE, function(match, pre, quot, dep, post) { deps.push(dep); }); @@ -669,7 +692,7 @@ function readAndStatDir(dir) { * Given a list of roots and list of extensions find all the files in * the directory with that extension and build a map of those assets. */ -function buildAssetMap(roots, processAsset) { +function buildAssetMap_DEPRECATED(roots, processAsset) { var queue = roots.slice(0); function search() { diff --git a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js index 2a24f2d13..8620d4883 100644 --- a/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js +++ b/packager/react-packager/src/DependencyResolver/haste/__tests__/HasteDependencyResolver-test.js @@ -10,7 +10,7 @@ jest.dontMock('../') .dontMock('q') - .dontMock('../requirePattern') + .dontMock('../replacePatterns') .setMock('../../ModuleDescriptor', function(data) {return data;}); var Promise = require('bluebird'); @@ -228,13 +228,148 @@ describe('HasteDependencyResolver', function() { var depGraph = depResolver._depGraph; var dependencies = ['x', 'y', 'z', 'a', 'b']; + + /*eslint-disable */ var code = [ + "import'x';", + "import 'x';", + "import 'x' ;", + "import Default from 'x';", + "import * as All from 'x';", + "import {} from 'x';", + "import { } from 'x';", + "import {Foo} from 'x';", + "import { Foo } from 'x';", + "import { Foo, } from 'x';", + "import {Foo as Bar} from 'x';", + "import { Foo as Bar } from 'x';", + "import { Foo as Bar, } from 'x';", + "import { Foo, Bar } from 'x';", + "import { Foo, Bar, } from 'x';", + "import { Foo as Bar, Baz } from 'x';", + "import { Foo as Bar, Baz, } from 'x';", + "import { Foo, Bar as Baz } from 'x';", + "import { Foo, Bar as Baz, } from 'x';", + "import { Foo as Bar, Baz as Qux } from 'x';", + "import { Foo as Bar, Baz as Qux, } from 'x';", + "import { Foo, Bar, Baz } from 'x';", + "import { Foo, Bar, Baz, } from 'x';", + "import { Foo as Bar, Baz, Qux } from 'x';", + "import { Foo as Bar, Baz, Qux, } from 'x';", + "import { Foo, Bar as Baz, Qux } from 'x';", + "import { Foo, Bar as Baz, Qux, } from 'x';", + "import { Foo, Bar, Baz as Qux } from 'x';", + "import { Foo, Bar, Baz as Qux, } from 'x';", + "import { Foo as Bar, Baz as Qux, Norf } from 'x';", + "import { Foo as Bar, Baz as Qux, Norf, } from 'x';", + "import { Foo as Bar, Baz, Qux as Norf } from 'x';", + "import { Foo as Bar, Baz, Qux as Norf, } from 'x';", + "import { Foo, Bar as Baz, Qux as Norf } from 'x';", + "import { Foo, Bar as Baz, Qux as Norf, } from 'x';", + "import { Foo as Bar, Baz as Qux, Norf as Enuf } from 'x';", + "import { Foo as Bar, Baz as Qux, Norf as Enuf, } from 'x';", + "import Default, * as All from 'x';", + "import Default, { } from 'x';", + "import Default, { Foo } from 'x';", + "import Default, { Foo, } from 'x';", + "import Default, { Foo as Bar } from 'x';", + "import Default, { Foo as Bar, } from 'x';", + "import Default, { Foo, Bar } from 'x';", + "import Default, { Foo, Bar, } from 'x';", + "import Default, { Foo as Bar, Baz } from 'x';", + "import Default, { Foo as Bar, Baz, } from 'x';", + "import Default, { Foo, Bar as Baz } from 'x';", + "import Default, { Foo, Bar as Baz, } from 'x';", + "import Default, { Foo as Bar, Baz as Qux } from 'x';", + "import Default, { Foo as Bar, Baz as Qux, } from 'x';", + "import Default, { Foo, Bar, Baz } from 'x';", + "import Default, { Foo, Bar, Baz, } from 'x';", + "import Default, { Foo as Bar, Baz, Qux } from 'x';", + "import Default, { Foo as Bar, Baz, Qux, } from 'x';", + "import Default, { Foo, Bar as Baz, Qux } from 'x';", + "import Default, { Foo, Bar as Baz, Qux, } from 'x';", + "import Default, { Foo, Bar, Baz as Qux } from 'x';", + "import Default, { Foo, Bar, Baz as Qux, } from 'x';", + "import Default, { Foo as Bar, Baz as Qux, Norf } from 'x';", + "import Default, { Foo as Bar, Baz as Qux, Norf, } from 'x';", + "import Default, { Foo as Bar, Baz, Qux as Norf } from 'x';", + "import Default, { Foo as Bar, Baz, Qux as Norf, } from 'x';", + "import Default, { Foo, Bar as Baz, Qux as Norf } from 'x';", + "import Default, { Foo, Bar as Baz, Qux as Norf, } from 'x';", + "import Default, { Foo as Bar, Baz as Qux, Norf as NoMore } from 'x';", + "import Default, { Foo as Bar, Baz as Qux, Norf as NoMore, } from 'x';", + "import Default , { } from 'x';", + 'import "x";', + 'import Default from "x";', + 'import * as All from "x";', + 'import { } from "x";', + 'import { Foo } from "x";', + 'import { Foo, } from "x";', + 'import { Foo as Bar } from "x";', + 'import { Foo as Bar, } from "x";', + 'import { Foo, Bar } from "x";', + 'import { Foo, Bar, } from "x";', + 'import { Foo as Bar, Baz } from "x";', + 'import { Foo as Bar, Baz, } from "x";', + 'import { Foo, Bar as Baz } from "x";', + 'import { Foo, Bar as Baz, } from "x";', + 'import { Foo as Bar, Baz as Qux } from "x";', + 'import { Foo as Bar, Baz as Qux, } from "x";', + 'import { Foo, Bar, Baz } from "x";', + 'import { Foo, Bar, Baz, } from "x";', + 'import { Foo as Bar, Baz, Qux } from "x";', + 'import { Foo as Bar, Baz, Qux, } from "x";', + 'import { Foo, Bar as Baz, Qux } from "x";', + 'import { Foo, Bar as Baz, Qux, } from "x";', + 'import { Foo, Bar, Baz as Qux } from "x";', + 'import { Foo, Bar, Baz as Qux, } from "x";', + 'import { Foo as Bar, Baz as Qux, Norf } from "x";', + 'import { Foo as Bar, Baz as Qux, Norf, } from "x";', + 'import { Foo as Bar, Baz, Qux as Norf } from "x";', + 'import { Foo as Bar, Baz, Qux as Norf, } from "x";', + 'import { Foo, Bar as Baz, Qux as Norf } from "x";', + 'import { Foo, Bar as Baz, Qux as Norf, } from "x";', + 'import { Foo as Bar, Baz as Qux, Norf as NoMore } from "x";', + 'import { Foo as Bar, Baz as Qux, Norf as NoMore, } from "x";', + 'import Default, * as All from "x";', + 'import Default, { } from "x";', + 'import Default, { Foo } from "x";', + 'import Default, { Foo, } from "x";', + 'import Default, { Foo as Bar } from "x";', + 'import Default, { Foo as Bar, } from "x";', + 'import Default, { Foo, Bar } from "x";', + 'import Default, { Foo, Bar, } from "x";', + 'import Default, { Foo as Bar, Baz } from "x";', + 'import Default, { Foo as Bar, Baz, } from "x";', + 'import Default, { Foo, Bar as Baz } from "x";', + 'import Default, { Foo, Bar as Baz, } from "x";', + 'import Default, { Foo as Bar, Baz as Qux } from "x";', + 'import Default, { Foo as Bar, Baz as Qux, } from "x";', + 'import Default, { Foo, Bar, Baz } from "x";', + 'import Default, { Foo, Bar, Baz, } from "x";', + 'import Default, { Foo as Bar, Baz, Qux } from "x";', + 'import Default, { Foo as Bar, Baz, Qux, } from "x";', + 'import Default, { Foo, Bar as Baz, Qux } from "x";', + 'import Default, { Foo, Bar as Baz, Qux, } from "x";', + 'import Default, { Foo, Bar, Baz as Qux } from "x";', + 'import Default, { Foo, Bar, Baz as Qux, } from "x";', + 'import Default, { Foo as Bar, Baz as Qux, Norf } from "x";', + 'import Default, { Foo as Bar, Baz as Qux, Norf, } from "x";', + 'import Default, { Foo as Bar, Baz, Qux as Norf } from "x";', + 'import Default, { Foo as Bar, Baz, Qux as Norf, } from "x";', + 'import Default, { Foo, Bar as Baz, Qux as Norf } from "x";', + 'import Default, { Foo, Bar as Baz, Qux as Norf, } from "x";', + 'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf } from "x";', + 'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf, } from "x";', + 'import Default from "y";', + 'import * as All from \'z\';', 'require("x")', 'require("y")', - 'require( "z" )', + 'require( \'z\' )', 'require( "a")', 'require("b" )', ].join('\n'); + /*eslint-disable */ depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) { if (toModuleName === 'x') { @@ -242,7 +377,7 @@ describe('HasteDependencyResolver', function() { id: 'changed' }; } else if (toModuleName === 'y') { - return { id: 'y' }; + return { id: 'Y' }; } return null; }); @@ -254,13 +389,145 @@ describe('HasteDependencyResolver', function() { }, code); expect(processedCode).toEqual([ - '__d(\'test module\',["changed","y"],function(global,' + - ' require, requireDynamic, requireLazy, module, exports) {' + - ' require(\'changed\')', - 'require(\'y\')', - 'require("z")', - 'require("a")', - 'require("b")});', + '__d(\'test module\',["changed","Y"],function(global,' + + ' require, requireDynamic, requireLazy, module, exports) { ' + + "import'x';", + "import 'changed';", + "import 'changed' ;", + "import Default from 'changed';", + "import * as All from 'changed';", + "import {} from 'changed';", + "import { } from 'changed';", + "import {Foo} from 'changed';", + "import { Foo } from 'changed';", + "import { Foo, } from 'changed';", + "import {Foo as Bar} from 'changed';", + "import { Foo as Bar } from 'changed';", + "import { Foo as Bar, } from 'changed';", + "import { Foo, Bar } from 'changed';", + "import { Foo, Bar, } from 'changed';", + "import { Foo as Bar, Baz } from 'changed';", + "import { Foo as Bar, Baz, } from 'changed';", + "import { Foo, Bar as Baz } from 'changed';", + "import { Foo, Bar as Baz, } from 'changed';", + "import { Foo as Bar, Baz as Qux } from 'changed';", + "import { Foo as Bar, Baz as Qux, } from 'changed';", + "import { Foo, Bar, Baz } from 'changed';", + "import { Foo, Bar, Baz, } from 'changed';", + "import { Foo as Bar, Baz, Qux } from 'changed';", + "import { Foo as Bar, Baz, Qux, } from 'changed';", + "import { Foo, Bar as Baz, Qux } from 'changed';", + "import { Foo, Bar as Baz, Qux, } from 'changed';", + "import { Foo, Bar, Baz as Qux } from 'changed';", + "import { Foo, Bar, Baz as Qux, } from 'changed';", + "import { Foo as Bar, Baz as Qux, Norf } from 'changed';", + "import { Foo as Bar, Baz as Qux, Norf, } from 'changed';", + "import { Foo as Bar, Baz, Qux as Norf } from 'changed';", + "import { Foo as Bar, Baz, Qux as Norf, } from 'changed';", + "import { Foo, Bar as Baz, Qux as Norf } from 'changed';", + "import { Foo, Bar as Baz, Qux as Norf, } from 'changed';", + "import { Foo as Bar, Baz as Qux, Norf as Enuf } from 'changed';", + "import { Foo as Bar, Baz as Qux, Norf as Enuf, } from 'changed';", + "import Default, * as All from 'changed';", + "import Default, { } from 'changed';", + "import Default, { Foo } from 'changed';", + "import Default, { Foo, } from 'changed';", + "import Default, { Foo as Bar } from 'changed';", + "import Default, { Foo as Bar, } from 'changed';", + "import Default, { Foo, Bar } from 'changed';", + "import Default, { Foo, Bar, } from 'changed';", + "import Default, { Foo as Bar, Baz } from 'changed';", + "import Default, { Foo as Bar, Baz, } from 'changed';", + "import Default, { Foo, Bar as Baz } from 'changed';", + "import Default, { Foo, Bar as Baz, } from 'changed';", + "import Default, { Foo as Bar, Baz as Qux } from 'changed';", + "import Default, { Foo as Bar, Baz as Qux, } from 'changed';", + "import Default, { Foo, Bar, Baz } from 'changed';", + "import Default, { Foo, Bar, Baz, } from 'changed';", + "import Default, { Foo as Bar, Baz, Qux } from 'changed';", + "import Default, { Foo as Bar, Baz, Qux, } from 'changed';", + "import Default, { Foo, Bar as Baz, Qux } from 'changed';", + "import Default, { Foo, Bar as Baz, Qux, } from 'changed';", + "import Default, { Foo, Bar, Baz as Qux } from 'changed';", + "import Default, { Foo, Bar, Baz as Qux, } from 'changed';", + "import Default, { Foo as Bar, Baz as Qux, Norf } from 'changed';", + "import Default, { Foo as Bar, Baz as Qux, Norf, } from 'changed';", + "import Default, { Foo as Bar, Baz, Qux as Norf } from 'changed';", + "import Default, { Foo as Bar, Baz, Qux as Norf, } from 'changed';", + "import Default, { Foo, Bar as Baz, Qux as Norf } from 'changed';", + "import Default, { Foo, Bar as Baz, Qux as Norf, } from 'changed';", + "import Default, { Foo as Bar, Baz as Qux, Norf as NoMore } from 'changed';", + "import Default, { Foo as Bar, Baz as Qux, Norf as NoMore, } from 'changed';", + "import Default , { } from 'changed';", + 'import "changed";', + 'import Default from "changed";', + 'import * as All from "changed";', + 'import { } from "changed";', + 'import { Foo } from "changed";', + 'import { Foo, } from "changed";', + 'import { Foo as Bar } from "changed";', + 'import { Foo as Bar, } from "changed";', + 'import { Foo, Bar } from "changed";', + 'import { Foo, Bar, } from "changed";', + 'import { Foo as Bar, Baz } from "changed";', + 'import { Foo as Bar, Baz, } from "changed";', + 'import { Foo, Bar as Baz } from "changed";', + 'import { Foo, Bar as Baz, } from "changed";', + 'import { Foo as Bar, Baz as Qux } from "changed";', + 'import { Foo as Bar, Baz as Qux, } from "changed";', + 'import { Foo, Bar, Baz } from "changed";', + 'import { Foo, Bar, Baz, } from "changed";', + 'import { Foo as Bar, Baz, Qux } from "changed";', + 'import { Foo as Bar, Baz, Qux, } from "changed";', + 'import { Foo, Bar as Baz, Qux } from "changed";', + 'import { Foo, Bar as Baz, Qux, } from "changed";', + 'import { Foo, Bar, Baz as Qux } from "changed";', + 'import { Foo, Bar, Baz as Qux, } from "changed";', + 'import { Foo as Bar, Baz as Qux, Norf } from "changed";', + 'import { Foo as Bar, Baz as Qux, Norf, } from "changed";', + 'import { Foo as Bar, Baz, Qux as Norf } from "changed";', + 'import { Foo as Bar, Baz, Qux as Norf, } from "changed";', + 'import { Foo, Bar as Baz, Qux as Norf } from "changed";', + 'import { Foo, Bar as Baz, Qux as Norf, } from "changed";', + 'import { Foo as Bar, Baz as Qux, Norf as NoMore } from "changed";', + 'import { Foo as Bar, Baz as Qux, Norf as NoMore, } from "changed";', + 'import Default, * as All from "changed";', + 'import Default, { } from "changed";', + 'import Default, { Foo } from "changed";', + 'import Default, { Foo, } from "changed";', + 'import Default, { Foo as Bar } from "changed";', + 'import Default, { Foo as Bar, } from "changed";', + 'import Default, { Foo, Bar } from "changed";', + 'import Default, { Foo, Bar, } from "changed";', + 'import Default, { Foo as Bar, Baz } from "changed";', + 'import Default, { Foo as Bar, Baz, } from "changed";', + 'import Default, { Foo, Bar as Baz } from "changed";', + 'import Default, { Foo, Bar as Baz, } from "changed";', + 'import Default, { Foo as Bar, Baz as Qux } from "changed";', + 'import Default, { Foo as Bar, Baz as Qux, } from "changed";', + 'import Default, { Foo, Bar, Baz } from "changed";', + 'import Default, { Foo, Bar, Baz, } from "changed";', + 'import Default, { Foo as Bar, Baz, Qux } from "changed";', + 'import Default, { Foo as Bar, Baz, Qux, } from "changed";', + 'import Default, { Foo, Bar as Baz, Qux } from "changed";', + 'import Default, { Foo, Bar as Baz, Qux, } from "changed";', + 'import Default, { Foo, Bar, Baz as Qux } from "changed";', + 'import Default, { Foo, Bar, Baz as Qux, } from "changed";', + 'import Default, { Foo as Bar, Baz as Qux, Norf } from "changed";', + 'import Default, { Foo as Bar, Baz as Qux, Norf, } from "changed";', + 'import Default, { Foo as Bar, Baz, Qux as Norf } from "changed";', + 'import Default, { Foo as Bar, Baz, Qux as Norf, } from "changed";', + 'import Default, { Foo, Bar as Baz, Qux as Norf } from "changed";', + 'import Default, { Foo, Bar as Baz, Qux as Norf, } from "changed";', + 'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf } from "changed";', + 'import Default, { Foo as Bar, Baz as Qux, Norf as Enuf, } from "changed";', + 'import Default from "Y";', + 'import * as All from \'z\';', + 'require("changed")', + 'require("Y")', + 'require( \'z\' )', + 'require( "a")', + 'require("b" )});', ].join('\n')); }); }); diff --git a/packager/react-packager/src/DependencyResolver/haste/index.js b/packager/react-packager/src/DependencyResolver/haste/index.js index 941a687ec..e700e5fd0 100644 --- a/packager/react-packager/src/DependencyResolver/haste/index.js +++ b/packager/react-packager/src/DependencyResolver/haste/index.js @@ -10,7 +10,7 @@ var path = require('path'); var DependencyGraph = require('./DependencyGraph'); -var requirePattern = require('./requirePattern'); +var replacePatterns = require('./replacePatterns'); var ModuleDescriptor = require('../ModuleDescriptor'); var declareOpts = require('../../lib/declareOpts'); @@ -61,7 +61,7 @@ function HasteDependencyResolver(options) { this._depGraph = new DependencyGraph({ roots: opts.projectRoots, - assetRoots: opts.assetRoots, + assetRoots_DEPRECATED: opts.assetRoots, ignoreFilePath: function(filepath) { return filepath.indexOf('__tests__') !== -1 || (opts.blacklistRE && opts.blacklistRE.test(filepath)); @@ -144,20 +144,20 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) { } } - var relativizedCode = - code.replace(requirePattern, function(codeMatch, _, depName) { - var depId = resolvedDeps[depName]; - if (depId != null) { - return 'require(\'' + depId + '\')'; - } else { - return codeMatch.replace(/\s+/g, ''); - } - }); + var relativizeCode = function(codeMatch, pre, quot, depName, post) { + var depId = resolvedDeps[depName]; + if (depId) { + return pre + quot + depId + post; + } else { + return codeMatch; + } + }; return DEFINE_MODULE_CODE.replace(DEFINE_MODULE_REPLACE_RE, function(key) { return { '_moduleName_': module.id, - '_code_': relativizedCode, + '_code_': code.replace(replacePatterns.IMPORT_RE, relativizeCode) + .replace(replacePatterns.REQUIRE_RE, relativizeCode), '_deps_': JSON.stringify(resolvedDepsArr), }[key]; }); diff --git a/packager/react-packager/src/DependencyResolver/haste/requirePattern.js b/packager/react-packager/src/DependencyResolver/haste/replacePatterns.js similarity index 68% rename from packager/react-packager/src/DependencyResolver/haste/requirePattern.js rename to packager/react-packager/src/DependencyResolver/haste/replacePatterns.js index 26d807ffd..cde2d873c 100644 --- a/packager/react-packager/src/DependencyResolver/haste/requirePattern.js +++ b/packager/react-packager/src/DependencyResolver/haste/replacePatterns.js @@ -9,6 +9,5 @@ 'use strict'; -var REQUIRE_RE = /\brequire\s*?\(\s*?([\'"])([^"\']+)\1\s*?\)/g; - -module.exports = REQUIRE_RE; +exports.IMPORT_RE = /(\bimport\s+?(?:.+\s+?from\s+?)?)(['"])([^'"]+)(\2)/g; +exports.REQUIRE_RE = /(\brequire\s*?\(\s*?)(['"])([^'"]+)(\2\s*?\))/g; diff --git a/packager/react-packager/src/JSTransformer/index.js b/packager/react-packager/src/JSTransformer/index.js index fde8336e0..abfae2482 100644 --- a/packager/react-packager/src/JSTransformer/index.js +++ b/packager/react-packager/src/JSTransformer/index.js @@ -61,9 +61,7 @@ function Transformer(options) { projectRoots: options.projectRoots, }); - if (options.transformModulePath == null) { - this._failedToStart = Promise.reject(new Error('No transfrom module')); - } else { + if (options.transformModulePath != null) { this._workers = workerFarm( {autoStart: true, maxConcurrentCallsPerWorker: 1}, options.transformModulePath @@ -83,8 +81,8 @@ Transformer.prototype.invalidateFile = function(filePath) { }; Transformer.prototype.loadFileAndTransform = function(filePath) { - if (this._failedToStart) { - return this._failedToStart; + if (this._transform == null) { + return Promise.reject(new Error('No transfrom module')); } var transform = this._transform; diff --git a/website/layout/AutodocsLayout.js b/website/layout/AutodocsLayout.js index 3bcaf0113..23b223814 100644 --- a/website/layout/AutodocsLayout.js +++ b/website/layout/AutodocsLayout.js @@ -240,7 +240,9 @@ var APIDoc = React.createClass({ render: function() { var content = this.props.content; if (!content.methods) { - return
Error
; + throw new Error( + 'No component methods found for ' + content.componentName + ); } return (
diff --git a/website/server/extractDocs.js b/website/server/extractDocs.js index f57c6dac5..31209329c 100644 --- a/website/server/extractDocs.js +++ b/website/server/extractDocs.js @@ -127,7 +127,6 @@ var components = [ var apis = [ '../Libraries/Utilities/AlertIOS.js', - '../Libraries/Animation/Animation.js', '../Libraries/AppRegistry/AppRegistry.js', '../Libraries/AppStateIOS/AppStateIOS.ios.js', '../Libraries/Storage/AsyncStorage.ios.js',