From 6a838a4201f99ae5e88b3684bb798d363815dd53 Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 30 Dec 2015 11:38:44 -0800 Subject: [PATCH] Consume react, fbjs from npm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: We don't (yet) treat these the same as any other modules because we still have special resolution rules for them in the packager allowing the use of `providesModule`, but I believe this allows people to use npm react in their RN projects and not have duplicate copies of React. Fixes facebook/react-native#2985. This relies on fbjs 0.6, which includes `.flow` files alongside the `.js` files to allow them to be typechecked without additional configuration. This also uses react 0.14.5, which shims a couple of files (as `.native.js`) to avoid DOM-specific bits. Once we fix these in React, we will use the same code on web and native. Hopefully we can also remove the packager support I'm adding here for `.native.js`. This diff is not the desired end state for us – ideally the packager would know nothing of react or fbjs, and we'll get there eventually by not relying on `providesModule` in order to load react and fbjs modules. (fbjs change posted here but not merged yet: https://github.com/facebook/fbjs/pull/84.) This should also allow relay to work seamlessly with RN, but I haven't verified this. public Reviewed By: sebmarkbage Differential Revision: D2786197 fb-gh-sync-id: ff50f28445e949edc9501f4b599df7970813870d --- .flowconfig | 24 +- .../RCTLoggingTests.m | 2 +- .../eventPlugins/ResponderEventPlugin.js | 612 ------------------ packager/blacklist.js | 77 ++- .../DependencyGraph/HasteMap.js | 33 +- .../DependencyGraph/ResolutionRequest.js | 5 + .../DependencyGraph/index.js | 5 +- packager/react-packager/src/Resolver/index.js | 5 +- 8 files changed, 89 insertions(+), 674 deletions(-) delete mode 100644 Libraries/vendor/react/browser/eventPlugins/ResponderEventPlugin.js diff --git a/.flowconfig b/.flowconfig index 503e5a92572b96..53bfd25208fb1b 100644 --- a/.flowconfig +++ b/.flowconfig @@ -14,17 +14,19 @@ # Ignore react and fbjs where there are overlaps, but don't ignore # anything that react-native relies on -.*/node_modules/fbjs-haste/.*/__tests__/.* -.*/node_modules/fbjs-haste/__forks__/Map.js -.*/node_modules/fbjs-haste/__forks__/Promise.js -.*/node_modules/fbjs-haste/__forks__/fetch.js -.*/node_modules/fbjs-haste/core/ExecutionEnvironment.js -.*/node_modules/fbjs-haste/core/isEmpty.js -.*/node_modules/fbjs-haste/crypto/crc32.js -.*/node_modules/fbjs-haste/stubs/ErrorUtils.js -.*/node_modules/react-haste/React.js -.*/node_modules/react-haste/renderers/dom/ReactDOM.js -.*/node_modules/react-haste/renderers/shared/event/eventPlugins/ResponderEventPlugin.js +.*/node_modules/fbjs/lib/Map.js +.*/node_modules/fbjs/lib/Promise.js +.*/node_modules/fbjs/lib/fetch.js +.*/node_modules/fbjs/lib/ExecutionEnvironment.js +.*/node_modules/fbjs/lib/isEmpty.js +.*/node_modules/fbjs/lib/crc32.js +.*/node_modules/fbjs/lib/ErrorUtils.js + +# Flow has a built-in definition for the 'react' module which we prefer to use +# over the currently-untyped source +.*/node_modules/react/react.js +.*/node_modules/react/lib/React.js +.*/node_modules/react/lib/ReactDOM.js # Ignore commoner tests .*/node_modules/commoner/test/.* diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m index 0011b0524cf41f..0f34cfb26e54a4 100644 --- a/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m +++ b/Examples/UIExplorer/UIExplorerIntegrationTests/RCTLoggingTests.m @@ -89,7 +89,7 @@ - (void)testLogging XCTAssertEqual(_lastLogLevel, RCTLogLevelError); XCTAssertEqual(_lastLogSource, RCTLogSourceJavaScript); - XCTAssertEqualObjects(_lastLogMessage, @"Invariant Violation: Invariant failed"); + XCTAssertEqualObjects(_lastLogMessage, @"Invariant failed"); [_bridge enqueueJSCall:@"LoggingTestModule.logErrorToConsole" args:@[@"Invoking console.error"]]; dispatch_semaphore_wait(_logSem, DISPATCH_TIME_FOREVER); diff --git a/Libraries/vendor/react/browser/eventPlugins/ResponderEventPlugin.js b/Libraries/vendor/react/browser/eventPlugins/ResponderEventPlugin.js deleted file mode 100644 index b84985e6ba10bd..00000000000000 --- a/Libraries/vendor/react/browser/eventPlugins/ResponderEventPlugin.js +++ /dev/null @@ -1,612 +0,0 @@ -/** - * Copyright 2013-2015, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule ResponderEventPlugin - */ - -'use strict'; - -var EventConstants = require('EventConstants'); -var EventPluginUtils = require('EventPluginUtils'); -var EventPropagators = require('EventPropagators'); -var ReactInstanceHandles = require('ReactInstanceHandles'); -var ResponderSyntheticEvent = require('ResponderSyntheticEvent'); -var ResponderTouchHistoryStore = require('ResponderTouchHistoryStore'); - -var accumulate = require('accumulate'); -var invariant = require('invariant'); -var keyOf = require('keyOf'); - -var isStartish = EventPluginUtils.isStartish; -var isMoveish = EventPluginUtils.isMoveish; -var isEndish = EventPluginUtils.isEndish; -var executeDirectDispatch = EventPluginUtils.executeDirectDispatch; -var hasDispatches = EventPluginUtils.hasDispatches; -var executeDispatchesInOrderStopAtTrue = - EventPluginUtils.executeDispatchesInOrderStopAtTrue; - -/** - * ID of element that should respond to touch/move types of interactions, as - * indicated explicitly by relevant callbacks. - */ -var responderID = null; - -/** - * Count of current touches. A textInput should become responder iff the - * the selection changes while there is a touch on the screen. - */ -var trackedTouchCount = 0; - -/** - * Last reported number of active touches. - */ -var previousActiveTouches = 0; - -var changeResponder = function(nextResponderID, blockNativeResponder) { - var oldResponderID = responderID; - responderID = nextResponderID; - if (ResponderEventPlugin.GlobalResponderHandler !== null) { - ResponderEventPlugin.GlobalResponderHandler.onChange( - oldResponderID, - nextResponderID, - blockNativeResponder - ); - } -}; - -var eventTypes = { - /** - * On a `touchStart`/`mouseDown`, is it desired that this element become the - * responder? - */ - startShouldSetResponder: { - phasedRegistrationNames: { - bubbled: keyOf({onStartShouldSetResponder: null}), - captured: keyOf({onStartShouldSetResponderCapture: null}), - }, - }, - - /** - * On a `scroll`, is it desired that this element become the responder? This - * is usually not needed, but should be used to retroactively infer that a - * `touchStart` had occured during momentum scroll. During a momentum scroll, - * a touch start will be immediately followed by a scroll event if the view is - * currently scrolling. - * - * TODO: This shouldn't bubble. - */ - scrollShouldSetResponder: { - phasedRegistrationNames: { - bubbled: keyOf({onScrollShouldSetResponder: null}), - captured: keyOf({onScrollShouldSetResponderCapture: null}), - }, - }, - - /** - * On text selection change, should this element become the responder? This - * is needed for text inputs or other views with native selection, so the - * JS view can claim the responder. - * - * TODO: This shouldn't bubble. - */ - selectionChangeShouldSetResponder: { - phasedRegistrationNames: { - bubbled: keyOf({onSelectionChangeShouldSetResponder: null}), - captured: keyOf({onSelectionChangeShouldSetResponderCapture: null}), - }, - }, - - /** - * On a `touchMove`/`mouseMove`, is it desired that this element become the - * responder? - */ - moveShouldSetResponder: { - phasedRegistrationNames: { - bubbled: keyOf({onMoveShouldSetResponder: null}), - captured: keyOf({onMoveShouldSetResponderCapture: null}), - }, - }, - - /** - * Direct responder events dispatched directly to responder. Do not bubble. - */ - responderStart: {registrationName: keyOf({onResponderStart: null})}, - responderMove: {registrationName: keyOf({onResponderMove: null})}, - responderEnd: {registrationName: keyOf({onResponderEnd: null})}, - responderRelease: {registrationName: keyOf({onResponderRelease: null})}, - responderTerminationRequest: { - registrationName: keyOf({onResponderTerminationRequest: null}), - }, - responderGrant: {registrationName: keyOf({onResponderGrant: null})}, - responderReject: {registrationName: keyOf({onResponderReject: null})}, - responderTerminate: {registrationName: keyOf({onResponderTerminate: null})}, -}; - -/** - * - * Responder System: - * ---------------- - * - * - A global, solitary "interaction lock" on a view. - * - If a node becomes the responder, it should convey visual feedback - * immediately to indicate so, either by highlighting or moving accordingly. - * - To be the responder means, that touches are exclusively important to that - * responder view, and no other view. - * - While touches are still occuring, the responder lock can be transfered to - * a new view, but only to increasingly "higher" views (meaning ancestors of - * the current responder). - * - * Responder being granted: - * ------------------------ - * - * - Touch starts, moves, and scrolls can cause an ID to become the responder. - * - We capture/bubble `startShouldSetResponder`/`moveShouldSetResponder` to - * the "appropriate place". - * - If nothing is currently the responder, the "appropriate place" is the - * initiating event's `targetID`. - * - If something *is* already the responder, the "appropriate place" is the - * first common ancestor of the event target and the current `responderID`. - * - Some negotiation happens: See the timing diagram below. - * - Scrolled views automatically become responder. The reasoning is that a - * platform scroll view that isn't built on top of the responder system has - * began scrolling, and the active responder must now be notified that the - * interaction is no longer locked to it - the system has taken over. - * - * - Responder being released: - * As soon as no more touches that *started* inside of descendents of the - * *current* responderID, an `onResponderRelease` event is dispatched to the - * current responder, and the responder lock is released. - * - * TODO: - * - on "end", a callback hook for `onResponderEndShouldRemainResponder` that - * determines if the responder lock should remain. - * - If a view shouldn't "remain" the responder, any active touches should by - * default be considered "dead" and do not influence future negotiations or - * bubble paths. It should be as if those touches do not exist. - * -- For multitouch: Usually a translate-z will choose to "remain" responder - * after one out of many touches ended. For translate-y, usually the view - * doesn't wish to "remain" responder after one of many touches end. - * - Consider building this on top of a `stopPropagation` model similar to - * `W3C` events. - * - Ensure that `onResponderTerminate` is called on touch cancels, whether or - * not `onResponderTerminationRequest` returns `true` or `false`. - * - */ - -/* Negotiation Performed - +-----------------------+ - / \ -Process low level events to + Current Responder + wantsResponderID -determine who to perform negot-| (if any exists at all) | -iation/transition | Otherwise just pass through| --------------------------------+----------------------------+------------------+ -Bubble to find first ID | | -to return true:wantsResponderID| | - | | - +-------------+ | | - | onTouchStart| | | - +------+------+ none | | - | return| | -+-----------v-------------+true| +------------------------+ | -|onStartShouldSetResponder|----->|onResponderStart (cur) |<-----------+ -+-----------+-------------+ | +------------------------+ | | - | | | +--------+-------+ - | returned true for| false:REJECT +-------->|onResponderReject - | wantsResponderID | | | +----------------+ - | (now attempt | +------------------+-----+ | - | handoff) | | onResponder | | - +------------------->| TerminationRequest| | - | +------------------+-----+ | - | | | +----------------+ - | true:GRANT +-------->|onResponderGrant| - | | +--------+-------+ - | +------------------------+ | | - | | onResponderTerminate |<-----------+ - | +------------------+-----+ | - | | | +----------------+ - | +-------->|onResponderStart| - | | +----------------+ -Bubble to find first ID | | -to return true:wantsResponderID| | - | | - +-------------+ | | - | onTouchMove | | | - +------+------+ none | | - | return| | -+-----------v-------------+true| +------------------------+ | -|onMoveShouldSetResponder |----->|onResponderMove (cur) |<-----------+ -+-----------+-------------+ | +------------------------+ | | - | | | +--------+-------+ - | returned true for| false:REJECT +-------->|onResponderRejec| - | wantsResponderID | | | +----------------+ - | (now attempt | +------------------+-----+ | - | handoff) | | onResponder | | - +------------------->| TerminationRequest| | - | +------------------+-----+ | - | | | +----------------+ - | true:GRANT +-------->|onResponderGrant| - | | +--------+-------+ - | +------------------------+ | | - | | onResponderTerminate |<-----------+ - | +------------------+-----+ | - | | | +----------------+ - | +-------->|onResponderMove | - | | +----------------+ - | | - | | - Some active touch started| | - inside current responder | +------------------------+ | - +------------------------->| onResponderEnd | | - | | +------------------------+ | - +---+---------+ | | - | onTouchEnd | | | - +---+---------+ | | - | | +------------------------+ | - +------------------------->| onResponderEnd | | - No active touches started| +-----------+------------+ | - inside current responder | | | - | v | - | +------------------------+ | - | | onResponderRelease | | - | +------------------------+ | - | | - + + */ - - - -/** - * A note about event ordering in the `EventPluginHub`. - * - * Suppose plugins are injected in the following order: - * - * `[R, S, C]` - * - * To help illustrate the example, assume `S` is `SimpleEventPlugin` (for - * `onClick` etc) and `R` is `ResponderEventPlugin`. - * - * "Deferred-Dispatched Events": - * - * - The current event plugin system will traverse the list of injected plugins, - * in order, and extract events by collecting the plugin's return value of - * `extractEvents()`. - * - These events that are returned from `extractEvents` are "deferred - * dispatched events". - * - When returned from `extractEvents`, deferred-dispatched events contain an - * "accumulation" of deferred dispatches. - * - These deferred dispatches are accumulated/collected before they are - * returned, but processed at a later time by the `EventPluginHub` (hence the - * name deferred). - * - * In the process of returning their deferred-dispatched events, event plugins - * themselves can dispatch events on-demand without returning them from - * `extractEvents`. Plugins might want to do this, so that they can use event - * dispatching as a tool that helps them decide which events should be extracted - * in the first place. - * - * "On-Demand-Dispatched Events": - * - * - On-demand-dispatched events are not returned from `extractEvents`. - * - On-demand-dispatched events are dispatched during the process of returning - * the deferred-dispatched events. - * - They should not have side effects. - * - They should be avoided, and/or eventually be replaced with another - * abstraction that allows event plugins to perform multiple "rounds" of event - * extraction. - * - * Therefore, the sequence of event dispatches becomes: - * - * - `R`s on-demand events (if any) (dispatched by `R` on-demand) - * - `S`s on-demand events (if any) (dispatched by `S` on-demand) - * - `C`s on-demand events (if any) (dispatched by `C` on-demand) - * - `R`s extracted events (if any) (dispatched by `EventPluginHub`) - * - `S`s extracted events (if any) (dispatched by `EventPluginHub`) - * - `C`s extracted events (if any) (dispatched by `EventPluginHub`) - * - * In the case of `ResponderEventPlugin`: If the `startShouldSetResponder` - * on-demand dispatch returns `true` (and some other details are satisfied) the - * `onResponderGrant` deferred dispatched event is returned from - * `extractEvents`. The sequence of dispatch executions in this case - * will appear as follows: - * - * - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand) - * - `touchStartCapture` (`EventPluginHub` dispatches as usual) - * - `touchStart` (`EventPluginHub` dispatches as usual) - * - `responderGrant/Reject` (`EventPluginHub` dispatches as usual) - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {string} topLevelTargetID ID of deepest React rendered element. - * @param {object} nativeEvent Native browser event. - * @return {*} An accumulation of synthetic events. - */ -function setResponderAndExtractTransfer( - topLevelType, - topLevelTargetID, - nativeEvent, - nativeEventTarget) { - var shouldSetEventType = - isStartish(topLevelType) ? eventTypes.startShouldSetResponder : - isMoveish(topLevelType) ? eventTypes.moveShouldSetResponder : - topLevelType === EventConstants.topLevelTypes.topSelectionChange ? - eventTypes.selectionChangeShouldSetResponder : - eventTypes.scrollShouldSetResponder; - - // TODO: stop one short of the the current responder. - var bubbleShouldSetFrom = !responderID ? - topLevelTargetID : - ReactInstanceHandles.getFirstCommonAncestorID(responderID, topLevelTargetID); - - // When capturing/bubbling the "shouldSet" event, we want to skip the target - // (deepest ID) if it happens to be the current responder. The reasoning: - // It's strange to get an `onMoveShouldSetResponder` when you're *already* - // the responder. - var skipOverBubbleShouldSetFrom = bubbleShouldSetFrom === responderID; - var shouldSetEvent = ResponderSyntheticEvent.getPooled( - shouldSetEventType, - bubbleShouldSetFrom, - nativeEvent, - nativeEventTarget - ); - shouldSetEvent.touchHistory = ResponderTouchHistoryStore.touchHistory; - if (skipOverBubbleShouldSetFrom) { - EventPropagators.accumulateTwoPhaseDispatchesSkipTarget(shouldSetEvent); - } else { - EventPropagators.accumulateTwoPhaseDispatches(shouldSetEvent); - } - var wantsResponderID = executeDispatchesInOrderStopAtTrue(shouldSetEvent); - if (!shouldSetEvent.isPersistent()) { - shouldSetEvent.constructor.release(shouldSetEvent); - } - - if (!wantsResponderID || wantsResponderID === responderID) { - return null; - } - var extracted; - var grantEvent = ResponderSyntheticEvent.getPooled( - eventTypes.responderGrant, - wantsResponderID, - nativeEvent, - nativeEventTarget - ); - grantEvent.touchHistory = ResponderTouchHistoryStore.touchHistory; - - EventPropagators.accumulateDirectDispatches(grantEvent); - var blockNativeResponder = executeDirectDispatch(grantEvent) === true; - if (responderID) { - - var terminationRequestEvent = ResponderSyntheticEvent.getPooled( - eventTypes.responderTerminationRequest, - responderID, - nativeEvent, - nativeEventTarget - ); - terminationRequestEvent.touchHistory = ResponderTouchHistoryStore.touchHistory; - EventPropagators.accumulateDirectDispatches(terminationRequestEvent); - var shouldSwitch = !hasDispatches(terminationRequestEvent) || - executeDirectDispatch(terminationRequestEvent); - if (!terminationRequestEvent.isPersistent()) { - terminationRequestEvent.constructor.release(terminationRequestEvent); - } - - if (shouldSwitch) { - var terminateType = eventTypes.responderTerminate; - var terminateEvent = ResponderSyntheticEvent.getPooled( - terminateType, - responderID, - nativeEvent, - nativeEventTarget - ); - terminateEvent.touchHistory = ResponderTouchHistoryStore.touchHistory; - EventPropagators.accumulateDirectDispatches(terminateEvent); - extracted = accumulate(extracted, [grantEvent, terminateEvent]); - changeResponder(wantsResponderID, blockNativeResponder); - } else { - var rejectEvent = ResponderSyntheticEvent.getPooled( - eventTypes.responderReject, - wantsResponderID, - nativeEvent, - nativeEventTarget - ); - rejectEvent.touchHistory = ResponderTouchHistoryStore.touchHistory; - EventPropagators.accumulateDirectDispatches(rejectEvent); - extracted = accumulate(extracted, rejectEvent); - } - } else { - extracted = accumulate(extracted, grantEvent); - changeResponder(wantsResponderID, blockNativeResponder); - } - return extracted; -} - -/** - * A transfer is a negotiation between a currently set responder and the next - * element to claim responder status. Any start event could trigger a transfer - * of responderID. Any move event could trigger a transfer. - * - * @param {string} topLevelType Record from `EventConstants`. - * @return {boolean} True if a transfer of responder could possibly occur. - */ -function canTriggerTransfer(topLevelType, topLevelTargetID, nativeEvent) { - return topLevelTargetID && ( - // responderIgnoreScroll: We are trying to migrate away from specifically tracking native scroll - // events here and responderIgnoreScroll indicates we will send topTouchCancel to handle - // canceling touch events instead - (topLevelType === EventConstants.topLevelTypes.topScroll && - !nativeEvent.responderIgnoreScroll) || - (trackedTouchCount > 0 && - topLevelType === EventConstants.topLevelTypes.topSelectionChange) || - isStartish(topLevelType) || - isMoveish(topLevelType) - ); -} - -/** - * Returns whether or not this touch end event makes it such that there are no - * longer any touches that started inside of the current `responderID`. - * - * @param {NativeEvent} nativeEvent Native touch end event. - * @return {boolean} Whether or not this touch end event ends the responder. - */ -function noResponderTouches(nativeEvent) { - var touches = nativeEvent.touches; - if (!touches || touches.length === 0) { - return true; - } - for (var i = 0; i < touches.length; i++) { - var activeTouch = touches[i]; - var target = activeTouch.target; - if (target !== null && target !== undefined && target !== 0) { - // Is the original touch location inside of the current responder? - var isAncestor = - ReactInstanceHandles.isAncestorIDOf( - responderID, - EventPluginUtils.getID(target) - ); - if (isAncestor) { - return false; - } - } - } - return true; -} - - -var ResponderEventPlugin = { - - getResponderID: function() { - return responderID; - }, - - eventTypes: eventTypes, - - /** - * We must be resilient to `topLevelTargetID` being `undefined` on - * `touchMove`, or `touchEnd`. On certain platforms, this means that a native - * scroll has assumed control and the original touch targets are destroyed. - * - * @param {string} topLevelType Record from `EventConstants`. - * @param {DOMEventTarget} topLevelTarget The listening component root node. - * @param {string} topLevelTargetID ID of `topLevelTarget`. - * @param {object} nativeEvent Native browser event. - * @return {*} An accumulation of synthetic events. - * @see {EventPluginHub.extractEvents} - */ - extractEvents: function( - topLevelType, - topLevelTarget, - topLevelTargetID, - nativeEvent, - nativeEventTarget) { - if (isStartish(topLevelType)) { - trackedTouchCount += 1; - } else if (isEndish(topLevelType)) { - trackedTouchCount -= 1; - invariant( - trackedTouchCount >= 0, - 'Ended a touch event which was not counted in trackedTouchCount.' - ); - } - - ResponderTouchHistoryStore.recordTouchTrack(topLevelType, nativeEvent, nativeEventTarget); - - var extracted = canTriggerTransfer(topLevelType, topLevelTargetID, nativeEvent) ? - setResponderAndExtractTransfer( - topLevelType, - topLevelTargetID, - nativeEvent, - nativeEventTarget) : - null; - // Responder may or may not have transfered on a new touch start/move. - // Regardless, whoever is the responder after any potential transfer, we - // direct all touch start/move/ends to them in the form of - // `onResponderMove/Start/End`. These will be called for *every* additional - // finger that move/start/end, dispatched directly to whoever is the - // current responder at that moment, until the responder is "released". - // - // These multiple individual change touch events are are always bookended - // by `onResponderGrant`, and one of - // (`onResponderRelease/onResponderTerminate`). - var isResponderTouchStart = responderID && isStartish(topLevelType); - var isResponderTouchMove = responderID && isMoveish(topLevelType); - var isResponderTouchEnd = responderID && isEndish(topLevelType); - var incrementalTouch = - isResponderTouchStart ? eventTypes.responderStart : - isResponderTouchMove ? eventTypes.responderMove : - isResponderTouchEnd ? eventTypes.responderEnd : - null; - - if (incrementalTouch) { - var gesture = - ResponderSyntheticEvent.getPooled( - incrementalTouch, - responderID, - nativeEvent, - nativeEventTarget - ); - gesture.touchHistory = ResponderTouchHistoryStore.touchHistory; - EventPropagators.accumulateDirectDispatches(gesture); - extracted = accumulate(extracted, gesture); - } - - var isResponderTerminate = - responderID && - topLevelType === EventConstants.topLevelTypes.topTouchCancel; - var isResponderRelease = - responderID && - !isResponderTerminate && - isEndish(topLevelType) && - noResponderTouches(nativeEvent); - var finalTouch = - isResponderTerminate ? eventTypes.responderTerminate : - isResponderRelease ? eventTypes.responderRelease : - null; - if (finalTouch) { - var finalEvent = - ResponderSyntheticEvent.getPooled(finalTouch, responderID, nativeEvent, nativeEventTarget); - finalEvent.touchHistory = ResponderTouchHistoryStore.touchHistory; - EventPropagators.accumulateDirectDispatches(finalEvent); - extracted = accumulate(extracted, finalEvent); - changeResponder(null); - } - - var numberActiveTouches = - ResponderTouchHistoryStore.touchHistory.numberActiveTouches; - if (ResponderEventPlugin.GlobalInteractionHandler && - numberActiveTouches !== previousActiveTouches) { - ResponderEventPlugin.GlobalInteractionHandler.onChange( - numberActiveTouches - ); - } - previousActiveTouches = numberActiveTouches; - - return extracted; - }, - - GlobalResponderHandler: null, - GlobalInteractionHandler: null, - - injection: { - /** - * @param {{onChange: (ReactID, ReactID) => void} GlobalResponderHandler - * Object that handles any change in responder. Use this to inject - * integration with an existing touch handling system etc. - */ - injectGlobalResponderHandler: function(GlobalResponderHandler) { - ResponderEventPlugin.GlobalResponderHandler = GlobalResponderHandler; - }, - - /** - * @param {{onChange: (numberActiveTouches) => void} GlobalInteractionHandler - * Object that handles any change in the number of active touches. - */ - injectGlobalInteractionHandler: function(GlobalInteractionHandler) { - ResponderEventPlugin.GlobalInteractionHandler = GlobalInteractionHandler; - }, - }, -}; - -module.exports = ResponderEventPlugin; diff --git a/packager/blacklist.js b/packager/blacklist.js index 863e2a9acc2508..05973c286e9e3c 100644 --- a/packager/blacklist.js +++ b/packager/blacklist.js @@ -13,36 +13,41 @@ var path = require('path'); // Don't forget to everything listed here to `testConfig.json` // modulePathIgnorePatterns. var sharedBlacklist = [ - 'node_modules/react-haste/renderers/shared/event/eventPlugins/ResponderEventPlugin.js', - 'node_modules/react-haste/React.js', - 'node_modules/react-haste/renderers/dom/ReactDOM.js', + /node_modules[/\\]react[/\\]dist[/\\].*/, + 'node_modules/react/lib/React.js', + 'node_modules/react/lib/ReactDOM.js', // For each of these fbjs files (especially the non-forks/stubs), we should // consider deleting the conflicting copy and just using the fbjs version. - 'node_modules/fbjs-haste/__forks__/Map.js', - 'node_modules/fbjs-haste/__forks__/Promise.js', - 'node_modules/fbjs-haste/__forks__/fetch.js', - 'node_modules/fbjs-haste/core/Deferred.js', - 'node_modules/fbjs-haste/core/PromiseMap.js', - 'node_modules/fbjs-haste/core/areEqual.js', - 'node_modules/fbjs-haste/core/flattenArray.js', - 'node_modules/fbjs-haste/core/isEmpty.js', - 'node_modules/fbjs-haste/core/removeFromArray.js', - 'node_modules/fbjs-haste/core/resolveImmediate.js', - 'node_modules/fbjs-haste/core/sprintf.js', - 'node_modules/fbjs-haste/crypto/crc32.js', - 'node_modules/fbjs-haste/fetch/fetchWithRetries.js', - 'node_modules/fbjs-haste/functional/everyObject.js', - 'node_modules/fbjs-haste/functional/filterObject.js', - 'node_modules/fbjs-haste/functional/forEachObject.js', - 'node_modules/fbjs-haste/functional/someObject.js', - 'node_modules/fbjs-haste/request/xhrSimpleDataSerializer.js', - 'node_modules/fbjs-haste/stubs/ErrorUtils.js', - 'node_modules/fbjs-haste/stubs/URI.js', - 'node_modules/fbjs-haste/useragent/UserAgent.js', - 'node_modules/fbjs-haste/utils/nullthrows.js', + // + // fbjs forks: + 'node_modules/fbjs/lib/Map.js', + 'node_modules/fbjs/lib/Promise.js', + 'node_modules/fbjs/lib/fetch.js', + // fbjs stubs: + 'node_modules/fbjs/lib/ErrorUtils.js', + 'node_modules/fbjs/lib/URI.js', + // fbjs modules: + 'node_modules/fbjs/lib/Deferred.js', + 'node_modules/fbjs/lib/PromiseMap.js', + 'node_modules/fbjs/lib/UserAgent.js', + 'node_modules/fbjs/lib/areEqual.js', + 'node_modules/fbjs/lib/base62.js', + 'node_modules/fbjs/lib/crc32.js', + 'node_modules/fbjs/lib/everyObject.js', + 'node_modules/fbjs/lib/fetchWithRetries.js', + 'node_modules/fbjs/lib/filterObject.js', + 'node_modules/fbjs/lib/flattenArray.js', + 'node_modules/fbjs/lib/forEachObject.js', + 'node_modules/fbjs/lib/isEmpty.js', + 'node_modules/fbjs/lib/nullthrows.js', + 'node_modules/fbjs/lib/removeFromArray.js', + 'node_modules/fbjs/lib/resolveImmediate.js', + 'node_modules/fbjs/lib/someObject.js', + 'node_modules/fbjs/lib/sprintf.js', + 'node_modules/fbjs/lib/xhrSimpleDataSerializer.js', - // Those conflicts with the ones in fbjs-haste/. We need to blacklist the + // Those conflicts with the ones in fbjs/. We need to blacklist the // internal version otherwise they won't work in open source. 'downstream/core/CSSCore.js', 'downstream/core/TouchEventUtils.js', @@ -63,11 +68,8 @@ var sharedBlacklist = [ 'downstream/core/invariant.js', 'downstream/core/nativeRequestAnimationFrame.js', 'downstream/core/toArray.js', -]; -// Raw unescaped patterns in case you need to use wildcards -var sharedBlacklistWildcards = [ - 'website\/node_modules\/.*', + /website\/node_modules\/.*/, ]; var platformBlacklists = { @@ -85,10 +87,16 @@ var platformBlacklists = { ], }; -function escapeRegExp(str) { - var escaped = str.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - // convert the '/' into an escaped local file separator - return escaped.replace(/\//g,'\\' + path.sep); +function escapeRegExp(pattern) { + if (Object.prototype.toString.call(pattern) === '[object RegExp]') { + return pattern.source; + } else if (typeof pattern === 'string') { + var escaped = pattern.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + // convert the '/' into an escaped local file separator + return escaped.replace(/\//g,'\\' + path.sep); + } else { + throw new Error('Unexpected packager blacklist pattern: ' + pattern); + } } function blacklist(platform, additionalBlacklist) { @@ -96,7 +104,6 @@ function blacklist(platform, additionalBlacklist) { (additionalBlacklist || []).concat(sharedBlacklist) .concat(platformBlacklists[platform] || []) .map(escapeRegExp) - .concat(sharedBlacklistWildcards) .join('|') + ')$' ); diff --git a/packager/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js b/packager/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js index 38d27e3e3b6638..2f0b26af4773d8 100644 --- a/packager/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js +++ b/packager/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js @@ -12,12 +12,20 @@ const getPlatformExtension = require('../lib/getPlatformExtension'); const Promise = require('promise'); const GENERIC_PLATFORM = 'generic'; +const NATIVE_PLATFORM = 'native'; class HasteMap { - constructor({ extensions, fastfs, moduleCache, helpers }) { + constructor({ + extensions, + fastfs, + moduleCache, + preferNativePlatform, + helpers, + }) { this._extensions = extensions; this._fastfs = fastfs; this._moduleCache = moduleCache; + this._preferNativePlatform = preferNativePlatform; this._helpers = helpers; } @@ -73,18 +81,19 @@ class HasteMap { return null; } - // If no platform is given we choose the generic platform module list. - // If a platform is given and no modules exist we fallback - // to the generic platform module list. - if (platform == null) { - return modulesMap[GENERIC_PLATFORM]; - } else { - let module = modulesMap[platform]; - if (module == null) { - module = modulesMap[GENERIC_PLATFORM]; - } - return module; + // If platform is 'ios', we prefer .ios.js to .native.js which we prefer to + // a plain .js file. + let module = undefined; + if (module == null && platform != null) { + module = modulesMap[platform]; + } + if (module == null && this._preferNativePlatform) { + module = modulesMap[NATIVE_PLATFORM]; + } + if (module == null) { + module = modulesMap[GENERIC_PLATFORM]; } + return module; } _processHasteModule(file) { diff --git a/packager/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js b/packager/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js index 2125b97f860238..f896de27c80c04 100644 --- a/packager/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js +++ b/packager/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js @@ -18,6 +18,7 @@ const Promise = require('promise'); class ResolutionRequest { constructor({ platform, + preferNativePlatform, entryPath, hasteMap, deprecatedAssetMap, @@ -26,6 +27,7 @@ class ResolutionRequest { fastfs, }) { this._platform = platform; + this._preferNativePlatform = preferNativePlatform; this._entryPath = entryPath; this._hasteMap = hasteMap; this._deprecatedAssetMap = deprecatedAssetMap; @@ -329,6 +331,9 @@ class ResolutionRequest { } else if (this._platform != null && this._fastfs.fileExists(potentialModulePath + '.' + this._platform + '.js')) { file = potentialModulePath + '.' + this._platform + '.js'; + } else if (this._preferNativePlatform && + this._fastfs.fileExists(potentialModulePath + '.native.js')) { + file = potentialModulePath + '.native.js'; } else if (this._fastfs.fileExists(potentialModulePath + '.js')) { file = potentialModulePath + '.js'; } else if (this._fastfs.fileExists(potentialModulePath + '.json')) { diff --git a/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js b/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js index ac88a2e37c9910..c4e983ad9072fe 100644 --- a/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js +++ b/packager/react-packager/src/DependencyResolver/DependencyGraph/index.js @@ -37,6 +37,7 @@ class DependencyGraph { assetExts, providesModuleNodeModules, platforms, + preferNativePlatform, cache, extensions, mocksPattern, @@ -51,6 +52,7 @@ class DependencyGraph { assetExts: assetExts || [], providesModuleNodeModules, platforms: platforms || [], + preferNativePlatform: preferNativePlatform || false, cache, extensions: extensions || ['js', 'json'], mocksPattern, @@ -105,7 +107,7 @@ class DependencyGraph { fastfs: this._fastfs, extensions: this._opts.extensions, moduleCache: this._moduleCache, - assetExts: this._opts.exts, + preferNativePlatform: this._opts.preferNativePlatform, helpers: this._helpers, }); @@ -147,6 +149,7 @@ class DependencyGraph { const absPath = this._getAbsolutePath(entryPath); const req = new ResolutionRequest({ platform, + preferNativePlatform: this._opts.preferNativePlatform, entryPath: absPath, deprecatedAssetMap: this._deprecatedAssetMap, hasteMap: this._hasteMap, diff --git a/packager/react-packager/src/Resolver/index.js b/packager/react-packager/src/Resolver/index.js index d0f9228569f403..ecc9eeebeb6201 100644 --- a/packager/react-packager/src/Resolver/index.js +++ b/packager/react-packager/src/Resolver/index.js @@ -81,8 +81,8 @@ class Resolver { (opts.blacklistRE && opts.blacklistRE.test(filepath)); }, providesModuleNodeModules: [ - 'fbjs-haste', - 'react-haste', + 'fbjs', + 'react', 'react-native', // Parse requires AsyncStorage. They will // change that to require('react-native') which @@ -92,6 +92,7 @@ class Resolver { 'react-transform-hmr', ], platforms: ['ios', 'android'], + preferNativePlatform: true, fileWatcher: opts.fileWatcher, cache: opts.cache, });