@samitouri / QOS-React / commits / 72434a7686

Remove or inline some fbjs dependencies (#13046)

Dan Abramov committed Jun 15, 2018 at 18:12 UTC 72434a7686035b4af766ee7d06c070d7f5d6a5f2
19 files changed +236 -50
packages/react-dom/src/client/ReactDOM.js
+2 -6
@@ -21,7 +21,7 @@ import './ReactDOMClientInjection';
21
22 import * as DOMRenderer from 'react-reconciler/inline.dom';
23 import * as ReactPortal from 'shared/ReactPortal';
24 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
24 +import {canUseDOM} from 'shared/ExecutionEnvironment';
25 import * as ReactGenericBatching from 'events/ReactGenericBatching';
26 import * as ReactControlledComponent from 'events/ReactControlledComponent';
27 import * as EventPluginHub from 'events/EventPluginHub';
@@ -768,11 +768,7 @@ const foundDevTools = DOMRenderer.injectIntoDevTools({
768 });
769
770 if (__DEV__) {
771 - if (
772 - !foundDevTools &&
773 - ExecutionEnvironment.canUseDOM &&
774 - window.top === window.self
775 - ) {
771 + if (!foundDevTools && canUseDOM && window.top === window.self) {
772 // If we're in Chrome or Firefox, provide a download link if not installed.
773 if (
774 (navigator.userAgent.indexOf('Chrome') > -1 &&
packages/react-dom/src/client/ReactInputSelection.js
+40 -3
@@ -5,11 +5,48 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import containsNode from 'fbjs/lib/containsNode';
9 -import getActiveElement from 'fbjs/lib/getActiveElement';
8 +import getActiveElement from './getActiveElement';
9
10 import * as ReactDOMSelection from './ReactDOMSelection';
12 -import {ELEMENT_NODE} from '../shared/HTMLNodeType';
11 +import {ELEMENT_NODE, TEXT_NODE} from '../shared/HTMLNodeType';
12 +
13 +// TODO: this code is originally inlined from fbjs.
14 +// It is likely that we don't actually need all these checks
15 +// for the particular use case in this file.
16 +function isNode(object) {
17 + const doc = object ? object.ownerDocument || object : document;
18 + const defaultView = doc.defaultView || window;
19 + return !!(
20 + object &&
21 + (typeof defaultView.Node === 'function'
22 + ? object instanceof defaultView.Node
23 + : typeof object === 'object' &&
24 + typeof object.nodeType === 'number' &&
25 + typeof object.nodeName === 'string')
26 + );
27 +}
28 +
29 +function isTextNode(object) {
30 + return isNode(object) && object.nodeType === TEXT_NODE;
31 +}
32 +
33 +function containsNode(outerNode, innerNode) {
34 + if (!outerNode || !innerNode) {
35 + return false;
36 + } else if (outerNode === innerNode) {
37 + return true;
38 + } else if (isTextNode(outerNode)) {
39 + return false;
40 + } else if (isTextNode(innerNode)) {
41 + return containsNode(outerNode, innerNode.parentNode);
42 + } else if ('contains' in outerNode) {
43 + return outerNode.contains(innerNode);
44 + } else if (outerNode.compareDocumentPosition) {
45 + return !!(outerNode.compareDocumentPosition(innerNode) & 16);
46 + } else {
47 + return false;
48 + }
49 +}
50
51 function isInDocument(node) {
52 return containsNode(document.documentElement, node);
packages/react-dom/src/client/getActiveElement.js new
+20
@@ -0,0 +1,20 @@
1 +/**
2 + * Copyright (c) 2015-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export default function getActiveElement(doc: ?Document): ?Element {
11 + doc = doc || (typeof document !== 'undefined' ? document : undefined);
12 + if (typeof doc === 'undefined') {
13 + return null;
14 + }
15 + try {
16 + return doc.activeElement || doc.body;
17 + } catch (e) {
18 + return doc.body;
19 + }
20 +}
packages/react-dom/src/client/getTextContentAccessor.js
+2 -2
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
8 +import {canUseDOM} from 'shared/ExecutionEnvironment';
9
10 let contentKey = null;
11
@@ -16,7 +16,7 @@ let contentKey = null;
16 * @internal
17 */
18 function getTextContentAccessor() {
19 - if (!contentKey && ExecutionEnvironment.canUseDOM) {
19 + if (!contentKey && canUseDOM) {
20 // Prefer textContent to innerText because many browsers support both but
21 // SVG <text> elements don't support innerText even when <div> does.
22 contentKey =
packages/react-dom/src/events/BeforeInputEventPlugin.js
+5 -6
@@ -8,7 +8,7 @@
8 import type {TopLevelType} from 'events/TopLevelEventTypes';
9
10 import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
11 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
11 +import {canUseDOM} from 'shared/ExecutionEnvironment';
12
13 import {
14 TOP_BLUR,
@@ -29,11 +29,10 @@ import SyntheticInputEvent from './SyntheticInputEvent';
29 const END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
30 const START_KEYCODE = 229;
31
32 -const canUseCompositionEvent =
33 - ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window;
32 +const canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
33
34 let documentMode = null;
36 -if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
35 +if (canUseDOM && 'documentMode' in document) {
36 documentMode = document.documentMode;
37 }
38
@@ -41,13 +40,13 @@ if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) {
40 // directly represent `beforeInput`. The IE `textinput` event is not as
41 // useful, so we don't use it.
42 const canUseTextInputEvent =
44 - ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode;
43 + canUseDOM && 'TextEvent' in window && !documentMode;
44
45 // In IE9+, we have access to composition events, but the data supplied
46 // by the native compositionend event may be incorrect. Japanese ideographic
47 // spaces, for instance (\u3000) are not recorded correctly.
48 const useFallbackCompositionData =
50 - ExecutionEnvironment.canUseDOM &&
49 + canUseDOM &&
50 (!canUseCompositionEvent ||
51 (documentMode && documentMode > 8 && documentMode <= 11));
52
packages/react-dom/src/events/ChangeEventPlugin.js
+2 -2
@@ -11,7 +11,7 @@ import {enqueueStateRestore} from 'events/ReactControlledComponent';
11 import {batchedUpdates} from 'events/ReactGenericBatching';
12 import SyntheticEvent from 'events/SyntheticEvent';
13 import isTextInputElement from 'shared/isTextInputElement';
14 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
14 +import {canUseDOM} from 'shared/ExecutionEnvironment';
15
16 import {
17 TOP_BLUR,
@@ -119,7 +119,7 @@ function getTargetInstForChangeEvent(topLevelType, targetInst) {
119 * SECTION: handle `input` event
120 */
121 let isInputEventSupported = false;
122 -if (ExecutionEnvironment.canUseDOM) {
122 +if (canUseDOM) {
123 // IE9 claims to support the input event but fails to trigger it when
124 // deleting text, so we ignore its input events.
125 isInputEventSupported =
packages/react-dom/src/events/SelectEventPlugin.js
+4 -6
@@ -6,11 +6,10 @@
6 */
7
8 import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
9 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
9 +import {canUseDOM} from 'shared/ExecutionEnvironment';
10 import SyntheticEvent from 'events/SyntheticEvent';
11 import isTextInputElement from 'shared/isTextInputElement';
12 -import getActiveElement from 'fbjs/lib/getActiveElement';
13 -import shallowEqual from 'fbjs/lib/shallowEqual';
12 +import shallowEqual from 'shared/shallowEqual';
13
14 import {
15 TOP_BLUR,
@@ -23,14 +22,13 @@ import {
22 TOP_SELECTION_CHANGE,
23 } from './DOMTopLevelEventTypes';
24 import {isListeningToAllDependencies} from './ReactBrowserEventEmitter';
25 +import getActiveElement from '../client/getActiveElement';
26 import {getNodeFromInstance} from '../client/ReactDOMComponentTree';
27 import * as ReactInputSelection from '../client/ReactInputSelection';
28 import {DOCUMENT_NODE} from '../shared/HTMLNodeType';
29
30 const skipSelectionChangeEvent =
31 - ExecutionEnvironment.canUseDOM &&
32 - 'documentMode' in document &&
33 - document.documentMode <= 11;
31 + canUseDOM && 'documentMode' in document && document.documentMode <= 11;
32
33 const eventTypes = {
34 select: {
packages/react-dom/src/events/TapEventPlugin.js
+14 -2
@@ -8,7 +8,6 @@
8 */
9
10 import {accumulateTwoPhaseDispatches} from 'events/EventPropagators';
11 -import TouchEventUtils from 'fbjs/lib/TouchEventUtils';
11 import type {TopLevelType} from 'events/TopLevelEventTypes';
12
13 import {
@@ -85,11 +84,24 @@ const Axis: AxisType = {
84 y: {page: 'pageY', client: 'clientY', envScroll: 'currentPageScrollTop'},
85 };
86
87 +function extractSingleTouch(nativeEvent) {
88 + // $FlowFixMe: figure out why this is missing
89 + const touches = nativeEvent.touches;
90 + // $FlowFixMe: figure out why this is missing
91 + const changedTouches = nativeEvent.changedTouches;
92 + const hasTouches = touches && touches.length > 0;
93 + const hasChangedTouches = changedTouches && changedTouches.length > 0;
94 +
95 + return !hasTouches && hasChangedTouches
96 + ? changedTouches[0]
97 + : hasTouches ? touches[0] : nativeEvent;
98 +}
99 +
100 function getAxisCoordOfEvent(
101 axis: AxisCoordinateData,
102 nativeEvent: _Touch,
103 ): number {
92 - const singleTouch = TouchEventUtils.extractSingleTouch(nativeEvent);
104 + const singleTouch = extractSingleTouch(nativeEvent);
105 if (singleTouch) {
106 return singleTouch[axis.page];
107 }
packages/react-dom/src/events/getVendorPrefixedEventName.js
+2 -2
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
8 +import {canUseDOM} from 'shared/ExecutionEnvironment';
9
10 /**
11 * Generate a mapping of standard vendor prefixes using the defined style property and event name.
@@ -49,7 +49,7 @@ let style = {};
49 /**
50 * Bootstrap if a DOM exists.
51 */
52 -if (ExecutionEnvironment.canUseDOM) {
52 +if (canUseDOM) {
53 style = document.createElement('div').style;
54
55 // On some platforms, in particular some releases of Android 4.x,
packages/react-dom/src/events/isEventSupported.js
+2 -5
@@ -5,7 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
8 +import {canUseDOM} from 'shared/ExecutionEnvironment';
9
10 /**
11 * Checks if an event is supported in the current execution environment.
@@ -22,10 +22,7 @@ import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
22 * @license Modernizr 3.0.0pre (Custom Build) | MIT
23 */
24 function isEventSupported(eventNameSuffix, capture) {
25 - if (
26 - !ExecutionEnvironment.canUseDOM ||
27 - (capture && !('addEventListener' in document))
28 - ) {
25 + if (!canUseDOM || (capture && !('addEventListener' in document))) {
26 return false;
27 }
28
packages/react-dom/src/server/ReactPartialRenderer.js
+10 -5
@@ -17,10 +17,8 @@ import type {
17 import React from 'react';
18 import emptyFunction from 'fbjs/lib/emptyFunction';
19 import emptyObject from 'fbjs/lib/emptyObject';
20 -import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName';
20 import invariant from 'fbjs/lib/invariant';
21 import lowPriorityWarning from 'shared/lowPriorityWarning';
23 -import memoizeStringOnly from 'fbjs/lib/memoizeStringOnly';
22 import warning from 'fbjs/lib/warning';
23 import checkPropTypes from 'prop-types/checkPropTypes';
24 import describeComponentFrame from 'shared/describeComponentFrame';
@@ -51,6 +49,7 @@ import {
49 import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
50 import assertValidProps from '../shared/assertValidProps';
51 import dangerousStyleValue from '../shared/dangerousStyleValue';
52 +import hyphenateStyleName from '../shared/hyphenateStyleName';
53 import isCustomComponent from '../shared/isCustomComponent';
54 import omittedCloseTags from '../shared/omittedCloseTags';
55 import warnValidStyle from '../shared/warnValidStyle';
@@ -162,9 +161,15 @@ function validateDangerousTag(tag) {
161 }
162 }
163
165 -const processStyleName = memoizeStringOnly(function(styleName) {
166 - return hyphenateStyleName(styleName);
167 -});
164 +const styleNameCache = {};
165 +const processStyleName = function(styleName) {
166 + if (styleNameCache.hasOwnProperty(styleName)) {
167 + return styleNameCache[styleName];
168 + }
169 + const result = hyphenateStyleName(styleName);
170 + styleNameCache[styleName] = result;
171 + return result;
172 +};
173
174 function createMarkupForStyles(styles): string | null {
175 let serialized = '';
packages/react-dom/src/shared/CSSPropertyOperations.js
+1 -1
@@ -6,7 +6,7 @@
6 */
7
8 import dangerousStyleValue from './dangerousStyleValue';
9 -import hyphenateStyleName from 'fbjs/lib/hyphenateStyleName';
9 +import hyphenateStyleName from './hyphenateStyleName';
10 import warnValidStyle from './warnValidStyle';
11
12 /**
packages/react-dom/src/shared/hyphenateStyleName.js new
+31
@@ -0,0 +1,31 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +const uppercasePattern = /([A-Z])/g;
11 +const msPattern = /^ms-/;
12 +
13 +/**
14 + * Hyphenates a camelcased CSS property name, for example:
15 + *
16 + * > hyphenateStyleName('backgroundColor')
17 + * < "background-color"
18 + * > hyphenateStyleName('MozTransition')
19 + * < "-moz-transition"
20 + * > hyphenateStyleName('msTransition')
21 + * < "-ms-transition"
22 + *
23 + * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
24 + * is converted to `-ms-`.
25 + */
26 +export default function hyphenateStyleName(name: string): string {
27 + return name
28 + .replace(uppercasePattern, '-$1')
29 + .toLowerCase()
30 + .replace(msPattern, '-ms-');
31 +}
packages/react-dom/src/shared/warnValidStyle.js
+12 -2
@@ -6,7 +6,6 @@
6 */
7
8 import emptyFunction from 'fbjs/lib/emptyFunction';
9 -import camelizeStyleName from 'fbjs/lib/camelizeStyleName';
9 import warning from 'fbjs/lib/warning';
10
11 let warnValidStyle = emptyFunction;
@@ -14,6 +13,8 @@ let warnValidStyle = emptyFunction;
13 if (__DEV__) {
14 // 'msTransform' is correct, but the other prefixes should be capitalized
15 const badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
16 + const msPattern = /^-ms-/;
17 + const hyphenPattern = /-(.)/g;
18
19 // style values shouldn't contain a semicolon
20 const badStyleValueWithSemicolonPattern = /;\s*$/;
@@ -23,6 +24,12 @@ if (__DEV__) {
24 let warnedForNaNValue = false;
25 let warnedForInfinityValue = false;
26
27 + const camelize = function(string) {
28 + return string.replace(hyphenPattern, function(_, character) {
29 + return character.toUpperCase();
30 + });
31 + };
32 +
33 const warnHyphenatedStyleName = function(name, getStack) {
34 if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
35 return;
@@ -33,7 +40,10 @@ if (__DEV__) {
40 false,
41 'Unsupported style property %s. Did you mean %s?%s',
42 name,
36 - camelizeStyleName(name),
43 + // As Andi Smith suggests
44 + // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
45 + // is converted to lowercase `ms`.
46 + camelize(name.replace(msPattern, 'ms-')),
47 getStack(),
48 );
49 };
packages/react-reconciler/src/ReactFiberClassComponent.js
+1 -1
@@ -19,9 +19,9 @@ import {
19 import ReactStrictModeWarnings from './ReactStrictModeWarnings';
20 import {isMounted} from 'react-reconciler/reflection';
21 import * as ReactInstanceMap from 'shared/ReactInstanceMap';
22 +import shallowEqual from 'shared/shallowEqual';
23 import emptyObject from 'fbjs/lib/emptyObject';
24 import getComponentName from 'shared/getComponentName';
24 -import shallowEqual from 'fbjs/lib/shallowEqual';
25 import invariant from 'fbjs/lib/invariant';
26 import warning from 'fbjs/lib/warning';
27
packages/react-scheduler/src/ReactScheduler.js
+3 -6
@@ -41,14 +41,11 @@ type CallbackConfigType = {|
41
42 export type CallbackIdType = CallbackConfigType;
43
44 -import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
44 +import {canUseDOM} from 'shared/ExecutionEnvironment';
45 import warning from 'fbjs/lib/warning';
46
47 if (__DEV__) {
48 - if (
49 - ExecutionEnvironment.canUseDOM &&
50 - typeof requestAnimationFrame !== 'function'
51 - ) {
48 + if (canUseDOM && typeof requestAnimationFrame !== 'function') {
49 warning(
50 false,
51 // TODO: reword this when schedule is a stand-alone module
@@ -87,7 +84,7 @@ let scheduleWork: (
84 ) => CallbackIdType;
85 let cancelScheduledWork: (callbackId: CallbackIdType) => void;
86
90 -if (!ExecutionEnvironment.canUseDOM) {
87 +if (!canUseDOM) {
88 const timeoutIds = new Map();
89
90 scheduleWork = function(
packages/react-test-renderer/src/ReactShallowRenderer.js
+1 -1
@@ -10,9 +10,9 @@ import React from 'react';
10 import {isForwardRef} from 'react-is';
11 import describeComponentFrame from 'shared/describeComponentFrame';
12 import getComponentName from 'shared/getComponentName';
13 +import shallowEqual from 'shared/shallowEqual';
14 import emptyObject from 'fbjs/lib/emptyObject';
15 import invariant from 'fbjs/lib/invariant';
15 -import shallowEqual from 'fbjs/lib/shallowEqual';
16 import checkPropTypes from 'prop-types/checkPropTypes';
17
18 class ReactShallowRenderer {
packages/shared/ExecutionEnvironment.js new
+14
@@ -0,0 +1,14 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export const canUseDOM: boolean = !!(
11 + typeof window !== 'undefined' &&
12 + window.document &&
13 + window.document.createElement
14 +);
packages/shared/shallowEqual.js new
+70
@@ -0,0 +1,70 @@
1 +/**
2 + * Copyright (c) 2013-present, Facebook, Inc.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +/*eslint-disable no-self-compare */
11 +
12 +const hasOwnProperty = Object.prototype.hasOwnProperty;
13 +
14 +/**
15 + * inlined Object.is polyfill to avoid requiring consumers ship their own
16 + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
17 + */
18 +function is(x, y) {
19 + // SameValue algorithm
20 + if (x === y) {
21 + // Steps 1-5, 7-10
22 + // Steps 6.b-6.e: +0 != -0
23 + // Added the nonzero y check to make Flow happy, but it is redundant
24 + return x !== 0 || y !== 0 || 1 / x === 1 / y;
25 + } else {
26 + // Step 6.a: NaN == NaN
27 + return x !== x && y !== y;
28 + }
29 +}
30 +
31 +/**
32 + * Performs equality by iterating through keys on an object and returning false
33 + * when any key has values which are not strictly equal between the arguments.
34 + * Returns true when the values of all keys are strictly equal.
35 + */
36 +function shallowEqual(objA: mixed, objB: mixed): boolean {
37 + if (is(objA, objB)) {
38 + return true;
39 + }
40 +
41 + if (
42 + typeof objA !== 'object' ||
43 + objA === null ||
44 + typeof objB !== 'object' ||
45 + objB === null
46 + ) {
47 + return false;
48 + }
49 +
50 + const keysA = Object.keys(objA);
51 + const keysB = Object.keys(objB);
52 +
53 + if (keysA.length !== keysB.length) {
54 + return false;
55 + }
56 +
57 + // Test for A's keys different from B.
58 + for (let i = 0; i < keysA.length; i++) {
59 + if (
60 + !hasOwnProperty.call(objB, keysA[i]) ||
61 + !is(objA[keysA[i]], objB[keysA[i]])
62 + ) {
63 + return false;
64 + }
65 + }
66 +
67 + return true;
68 +}
69 +
70 +export default shallowEqual;