From 85e1ad6b61b6c1e05bf2a9b80e99ec5e796111ee Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Mon, 6 Apr 2015 08:38:56 -0700 Subject: [PATCH] Updates from Mon 6 Apr - [ReactNative] Revert D1965911 | Christopher Chedeau - [ReactNative] Remove experimental Portal only needed for android right now. | Spencer Ahrens - [ReactNative] rename Animation to AnimationExperimental with warning docs | Spencer Ahrens - navigator.getCurrentRoutes() | Eric Vicenti - Fixing jsdoc parsing of functions that are defined over multiple lines (Fixes #410) | Christopher Chedeau - Added constraint of child type to touchablewithoutfeedback | Christopher Chedeau - [react-packager] Deprecate global image namespace in favor of CommonJS resolution | Amjad Masad - [react-packager] Don't cache rejected promise | Amjad Masad - [ReactNative] Start Navigator gesture config, disable gesture in AdsManager | Eric Vicenti - [Flow] Clean react-native-github for Flow v0.8.0 | Gabe Levi - add maximumValue and minimumValue as valid attributes for native Slider | Christopher Chedeau - react-packager: Add ES6 import statement support to DependencyGraph. | Amjad Masad - Remove false annotation | Christopher Chedeau - [madman] prevent pulling the content down inconsistently when the keyboard shows up | Kevin Gozali - add @flow back to View.js | Basil Hosmer - [ReactNative] Turn of lint warning for constant conditions | Eric Vicenti - [UIExplorer] Fixed 'Push View Example' in NavigatorIOS example | Christopher Chedeau - SliderIOS.js comments - grammar correction | Christopher Chedeau --- .eslintrc | 2 +- Examples/2048/2048.xcodeproj/project.pbxproj | 64 ++-- Examples/2048/Game2048.js | 10 +- .../UIExplorer/Navigator/JumpingNavSample.js | 53 ++-- Examples/UIExplorer/NavigatorIOSExample.js | 3 +- Examples/UIExplorer/ResponderExample.js | 2 +- ...{Animation.js => AnimationExperimental.js} | 26 +- ...Mixin.js => AnimationExperimentalMixin.js} | 15 +- .../project.pbxproj | 36 +-- ...er.h => RCTAnimationExperimentalManager.h} | 2 +- ...er.m => RCTAnimationExperimentalManager.m} | 12 +- Libraries/Components/ScrollResponder.js | 21 +- Libraries/Components/SliderIOS/SliderIOS.js | 13 +- Libraries/Components/TextInput/TextInput.js | 2 - .../Components/Touchable/TouchableBounce.js | 4 +- .../Touchable/TouchableWithoutFeedback.js | 5 +- Libraries/Components/View/View.js | 1 + .../ListView/ListViewDataSource.js | 149 +++++---- .../CustomComponents/Navigator/Navigator.js | 189 +++++++++--- .../Navigator/NavigatorSceneConfigs.js | 121 ++++---- Libraries/Portal/Portal.js | 70 ----- ...pplication.js => renderApplication.ios.js} | 24 +- Libraries/Utilities/AlertIOS.js | 42 ++- Libraries/react-native/react-native.js | 1 - React.podspec | 4 +- packager/README.md | 2 +- .../__tests__/DependencyGraph-test.js | 152 +++++++++- .../haste/DependencyGraph/index.js | 81 +++-- .../__tests__/HasteDependencyResolver-test.js | 287 +++++++++++++++++- .../src/DependencyResolver/haste/index.js | 24 +- .../{requirePattern.js => replacePatterns.js} | 5 +- .../react-packager/src/JSTransformer/index.js | 8 +- 32 files changed, 966 insertions(+), 464 deletions(-) rename Libraries/Animation/{Animation.js => AnimationExperimental.js} (56%) rename Libraries/Animation/{AnimationMixin.js => AnimationExperimentalMixin.js} (74%) rename Libraries/Animation/{RCTAnimation.xcodeproj => RCTAnimationExperimental.xcodeproj}/project.pbxproj (80%) rename Libraries/Animation/{RCTAnimationManager.h => RCTAnimationExperimentalManager.h} (85%) rename Libraries/Animation/{RCTAnimationManager.m => RCTAnimationExperimentalManager.m} (94%) mode change 100644 => 100755 Libraries/Components/Touchable/TouchableWithoutFeedback.js delete mode 100644 Libraries/Portal/Portal.js rename Libraries/ReactIOS/{renderApplication.js => renderApplication.ios.js} (59%) rename packager/react-packager/src/DependencyResolver/haste/{requirePattern.js => replacePatterns.js} (68%) diff --git a/.eslintrc b/.eslintrc index 8de521e669..a81fb56f31 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 18f1ec2a96..d792a2d621 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 03abfcd69f..a6e12ff131 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 b38d9291d0..eecabcbce9 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 a7acdde24d..eee731bd53 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 4cc62ff3aa..05ba27cd2b 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 019802f5e6..79daa4550f 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 8cb9680eff..7cee9b72b1 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 81eb52c705..038ec72520 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 1211143da3..0affc5233f 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 a6e342db6f..88bb5fe287 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 009bbebb45..1f3d493f3c 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 a8f2f51251..ae2475d336 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 0b30fd7a57..dc29af70af 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 d575657e9e..ffcc8e737c 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 1b1178ebbe..cd9ea02fdf --- 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 e34c13883c..c981e41294 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 2f57597688..8e8000781c 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 acf9b383fe..89f3b49c23 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 89e5de9e16..3072074b2a 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 762af36614..0000000000 --- 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 dbec1e853a..084390ac50 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 f9a8df60de..de72d7b9f0 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 46148bd86d..1aa978f46c 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 8abc2a5ba7..65dc93eb09 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/packager/README.md b/packager/README.md index f85d148d1a..8f9f649bf5 100644 --- a/packager/README.md +++ b/packager/README.md @@ -99,7 +99,7 @@ middleware. Takes the following options: * `projectRoots` array (required): Is the roots where your JavaScript file will exist -* `blacklistRE` regexp: Is a pattern to ignore certain paths from the +* `blacklistRE` regexp: Is a patter to ignore certain paths from the packager * `polyfillModuleName` array: Paths to polyfills you want to be included at the start of the bundle 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 5f6ec8822d..b6a978c632 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 9ca430c49c..3348907f18 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 2a24f2d137..8620d48830 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 941a687ecb..e700e5fd02 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 26d807ffd6..cde2d873ca 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 fde8336e04..abfae2482f 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;