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;