From 60016c448bb7d19fc989acd05dda5aca2e124381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Tue, 25 Feb 2020 13:54:27 -0800 Subject: [PATCH] Export React as Named Exports instead of CommonJS (#18106) * Add options for forked entry points We currently fork .fb.js entry points. This adds a few more options. .modern.fb.js - experimental FB builds .classic.fb.js - stable FB builds .fb.js - if no other FB build, use this for FB builds .experimental.js - experimental builds .stable.js - stable builds .js - used if no other override exists This will be used to have different ES exports for different builds. * Switch React to named exports * Export named exports from the export point itself We need to re-export the Flow exported types so we can use them in our code. We don't want to use the Flow types from upstream since it doesn't have the non-public APIs that we have. This should be able to use export * but I don't know why it doesn't work. This actually enables Flow typing of React which was just "any" before. This exposed some Flow errors that needs fixing. * Create forks for the react entrypoint None of our builds expose all exports and they all differ in at least one way, so we need four forks. * Set esModule flag to false We don't want to emit the esModule compatibility flag on our CommonJS output. For now we treat our named exports as if they're CommonJS. This is a potentially breaking change for scheduler (but all those apis are unstable), react-is and use-subscription. However, it seems unlikely that anyone would rely on this since these only have named exports. * Remove unused Feature Flags * Let jest observe the stable fork for stable tests This lets it do the negative test by ensuring that the right tests fail. However, this in turn will make other tests that are not behind __EXPERIMENTAL__ fail. So I need to do that next. * Put all tests that depend on exports behind __EXPERIMENTAL__ Since there's no way to override the exports using feature flags in .intern.js anymore we can't use these APIs in stable. The tradeoff here is that we can either enable the negative tests on "stable" that means experimental are expected to fail, or we can disable tests on stable. This is unfortunate since some of these APIs now run on a "stable" config at FB instead of the experimental. * Switch ReactDOM to named exports Same strategy as React. I moved the ReactDOMFB runtime injection to classic.fb.js Since we only fork the entrypoint, the `/testing` entrypoint needs to be forked too to re-export the same things plus `act`. This is a bit unfortunate. If it becomes a pattern we can consider forking in the module resolution deeply. fix flow * Fix ReactDOM Flow Types Now that ReactDOM is Flow type checked we need to fix up its types. * Configure jest to use stable entry for ReactDOM in non-experimental * Remove additional FeatureFlags that are no longer needed These are only flagging the exports and no implementation details so we can control them fully through the export overrides. --- .../ReactHooksInspection-test.internal.js | 5 + .../devtools/ContextMenu/useContextMenu.js | 12 +- packages/react-dom/index.classic.fb.js | 46 +++ packages/react-dom/index.experimental.js | 34 ++ packages/react-dom/index.fb.js | 14 - packages/react-dom/index.js | 8 +- packages/react-dom/index.modern.fb.js | 22 ++ packages/react-dom/index.stable.js | 24 ++ .../src/__tests__/ReactDOMFiber-test.js | 4 +- ...DOMServerPartialHydration-test.internal.js | 196 ++++----- ...MServerSelectiveHydration-test.internal.js | 372 +++++++++--------- .../renderSubtreeIntoContainer-test.js | 2 +- packages/react-dom/src/client/ReactDOM.js | 185 +++++---- packages/react-dom/src/client/ReactDOMFB.js | 36 -- .../src/client/ReactDOMHostConfig.js | 4 +- .../react-dom/src/client/ReactDOMLegacy.js | 1 + ...edDOMEventResponderSystem-test.internal.js | 5 + packages/react-dom/testing.classic.fb.js | 11 + packages/react-dom/testing.experimental.js | 11 + packages/react-dom/testing.js | 9 +- packages/react-dom/testing.modern.fb.js | 11 + packages/react-dom/testing.stable.js | 11 + .../__tests__/ContextMenu-test.internal.js | 5 + .../src/dom/__tests__/Focus-test.internal.js | 5 + .../__tests__/FocusWithin-test.internal.js | 5 + .../src/dom/__tests__/Hover-test.internal.js | 5 + .../src/dom/__tests__/Input-test.internal.js | 5 + .../dom/__tests__/Keyboard-test.internal.js | 5 + .../MixedResponders-test-internal.js | 5 + .../src/dom/__tests__/Press-test.internal.js | 5 + .../__tests__/PressLegacy-test.internal.js | 5 + .../src/dom/__tests__/Tap-test.internal.js | 5 + .../react-is/src/__tests__/ReactIs-test.js | 4 +- .../react-native-renderer/src/ReactFabric.js | 3 +- .../src/ReactFabricHostConfig.js | 4 +- .../src/ReactNativeFiberHostComponent.js | 11 +- .../src/ReactNativeHostConfig.js | 9 +- .../src/ReactNativeTypes.js | 4 +- .../ReactFiberFundamental-test.internal.js | 5 + .../src/__tests__/ReactScope-test.internal.js | 5 + packages/react/index.classic.fb.js | 55 +++ packages/react/index.experimental.js | 46 +++ packages/react/index.js | 76 +++- packages/react/index.modern.fb.js | 54 +++ packages/react/index.stable.js | 39 ++ packages/react/src/React.js | 139 +++---- .../react/src/__tests__/ReactElement-test.js | 6 +- .../ReactElementJSX-test.internal.js | 13 +- .../ReactElementValidator-test.internal.js | 2 +- packages/shared/ReactFeatureFlags.js | 20 - packages/shared/ReactSharedInternals.js | 2 + .../forks/ReactFeatureFlags.native-fb.js | 7 - .../forks/ReactFeatureFlags.native-oss.js | 7 - .../forks/ReactFeatureFlags.persistent.js | 7 - .../forks/ReactFeatureFlags.test-renderer.js | 7 - .../ReactFeatureFlags.test-renderer.www.js | 7 - .../shared/forks/ReactFeatureFlags.testing.js | 7 - .../forks/ReactFeatureFlags.testing.www.js | 7 - .../shared/forks/ReactFeatureFlags.www.js | 14 - scripts/jest/config.source-persistent.js | 21 + scripts/jest/config.source.js | 21 + scripts/rollup/build.js | 68 +++- 62 files changed, 1073 insertions(+), 670 deletions(-) create mode 100644 packages/react-dom/index.classic.fb.js create mode 100644 packages/react-dom/index.experimental.js delete mode 100644 packages/react-dom/index.fb.js create mode 100644 packages/react-dom/index.modern.fb.js create mode 100644 packages/react-dom/index.stable.js delete mode 100644 packages/react-dom/src/client/ReactDOMFB.js create mode 100644 packages/react-dom/testing.classic.fb.js create mode 100644 packages/react-dom/testing.experimental.js create mode 100644 packages/react-dom/testing.modern.fb.js create mode 100644 packages/react-dom/testing.stable.js create mode 100644 packages/react/index.classic.fb.js create mode 100644 packages/react/index.experimental.js create mode 100644 packages/react/index.modern.fb.js create mode 100644 packages/react/index.stable.js diff --git a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js index 3e1c67087c12a..d075b7097de2a 100644 --- a/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js +++ b/packages/react-debug-tools/src/__tests__/ReactHooksInspection-test.internal.js @@ -22,6 +22,11 @@ describe('ReactHooksInspection', () => { ReactDebugTools = require('react-debug-tools'); }); + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + it('should inspect a simple useResponder hook', () => { const TestResponder = React.DEPRECATED_createResponder('TestResponder', {}); diff --git a/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js b/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js index 1639befc76bb9..3829a4377ee34 100644 --- a/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js +++ b/packages/react-devtools-shared/src/devtools/ContextMenu/useContextMenu.js @@ -19,18 +19,22 @@ export default function useContextMenu({ }: {| data: Object, id: string, - ref: ElementRef, + ref: {current: ElementRef<'div'> | null}, |}) { const {showMenu} = useContext(RegistryContext); useEffect(() => { if (ref.current !== null) { - const handleContextMenu = event => { + const handleContextMenu = (event: MouseEvent | TouchEvent) => { event.preventDefault(); event.stopPropagation(); - const pageX = event.pageX || (event.touches && event.touches[0].pageX); - const pageY = event.pageY || (event.touches && event.touches[0].pageY); + const pageX = + event.pageX || + (event.touches && ((event: any): TouchEvent).touches[0].pageX); + const pageY = + event.pageY || + (event.touches && ((event: any): TouchEvent).touches[0].pageY); showMenu({data, id, pageX, pageY}); }; diff --git a/packages/react-dom/index.classic.fb.js b/packages/react-dom/index.classic.fb.js new file mode 100644 index 0000000000000..96ce6a6cc35c8 --- /dev/null +++ b/packages/react-dom/index.classic.fb.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +import {addUserTimingListener} from 'shared/ReactFeatureFlags'; +import {isEnabled} from './src/events/ReactDOMEventListener'; +import {getClosestInstanceFromNode} from './src/client/ReactDOMComponentTree'; + +import {__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/client/ReactDOM'; + +// For classic WWW builds, include a few internals that are already in use. +Object.assign((__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: any), { + ReactBrowserEventEmitter: { + isEnabled, + }, + ReactDOMComponentTree: { + getClosestInstanceFromNode, + }, + // Perf experiment + addUserTimingListener, +}); + +export { + createPortal, + unstable_batchedUpdates, + flushSync, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + version, + findDOMNode, + hydrate, + render, + unmountComponentAtNode, + createRoot, + createBlockingRoot, + unstable_discreteUpdates, + unstable_flushDiscreteUpdates, + unstable_flushControlled, + unstable_scheduleHydration, + unstable_renderSubtreeIntoContainer, + unstable_createPortal, +} from './src/client/ReactDOM'; diff --git a/packages/react-dom/index.experimental.js b/packages/react-dom/index.experimental.js new file mode 100644 index 0000000000000..b771d0105b8e5 --- /dev/null +++ b/packages/react-dom/index.experimental.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + createPortal, + unstable_batchedUpdates, + flushSync, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + version, + // Disabled behind disableLegacyReactDOMAPIs + findDOMNode, + hydrate, + render, + unmountComponentAtNode, + // exposeConcurrentModeAPIs + createRoot, + createBlockingRoot, + unstable_discreteUpdates, + unstable_flushDiscreteUpdates, + unstable_flushControlled, + unstable_scheduleHydration, + // Disabled behind disableUnstableRenderSubtreeIntoContainer + unstable_renderSubtreeIntoContainer, + // Disabled behind disableUnstableCreatePortal + // Temporary alias since we already shipped React 16 RC with it. + // TODO: remove in React 17. + unstable_createPortal, +} from './src/client/ReactDOM'; diff --git a/packages/react-dom/index.fb.js b/packages/react-dom/index.fb.js deleted file mode 100644 index ea901748d6949..0000000000000 --- a/packages/react-dom/index.fb.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -'use strict'; - -const ReactDOMFB = require('./src/client/ReactDOMFB'); - -// TODO: decide on the top-level export form. -// This is hacky but makes it work with both Rollup and Jest. -module.exports = ReactDOMFB.default || ReactDOMFB; diff --git a/packages/react-dom/index.js b/packages/react-dom/index.js index 2a016ba16e9db..3714de11244ff 100644 --- a/packages/react-dom/index.js +++ b/packages/react-dom/index.js @@ -7,10 +7,4 @@ * @flow */ -'use strict'; - -const ReactDOM = require('./src/client/ReactDOM'); - -// TODO: decide on the top-level export form. -// This is hacky but makes it work with both Rollup and Jest. -module.exports = ReactDOM.default || ReactDOM; +export * from './src/client/ReactDOM'; diff --git a/packages/react-dom/index.modern.fb.js b/packages/react-dom/index.modern.fb.js new file mode 100644 index 0000000000000..bcb213ad4ee35 --- /dev/null +++ b/packages/react-dom/index.modern.fb.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + createPortal, + unstable_batchedUpdates, + flushSync, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + version, + createRoot, + createBlockingRoot, + unstable_discreteUpdates, + unstable_flushDiscreteUpdates, + unstable_flushControlled, + unstable_scheduleHydration, +} from './src/client/ReactDOM'; diff --git a/packages/react-dom/index.stable.js b/packages/react-dom/index.stable.js new file mode 100644 index 0000000000000..00d52806bab59 --- /dev/null +++ b/packages/react-dom/index.stable.js @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + createPortal, + unstable_batchedUpdates, + flushSync, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + version, + findDOMNode, + hydrate, + render, + unmountComponentAtNode, + unstable_renderSubtreeIntoContainer, + // Temporary alias since we already shipped React 16 RC with it. + // TODO: remove in React 17. + unstable_createPortal, +} from './src/client/ReactDOM'; diff --git a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js index efc605a9edb3a..ef872dfeb47e0 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFiber-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFiber-test.js @@ -13,8 +13,6 @@ const React = require('react'); const ReactDOM = require('react-dom'); const PropTypes = require('prop-types'); -const ReactFeatureFlags = require('shared/ReactFeatureFlags'); - describe('ReactDOMFiber', () => { let container; @@ -249,7 +247,7 @@ describe('ReactDOMFiber', () => { }); // TODO: remove in React 17 - if (!ReactFeatureFlags.disableUnstableCreatePortal) { + if (!__EXPERIMENTAL__) { it('should support unstable_createPortal alias', () => { const portalContainer = document.createElement('div'); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js index 7a25979987b4f..f6f7c4979bc91 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js @@ -86,8 +86,6 @@ describe('ReactDOMServerPartialHydration', () => { Scheduler = require('scheduler'); Suspense = React.Suspense; SuspenseList = React.SuspenseList; - - useHover = require('react-interactions/events/hover').useHover; }); if (!__EXPERIMENTAL__) { @@ -2368,120 +2366,124 @@ describe('ReactDOMServerPartialHydration', () => { document.body.removeChild(container); }); - it('blocks only on the last continuous event (Responder system)', async () => { - let suspend1 = false; - let resolve1; - let promise1 = new Promise(resolvePromise => (resolve1 = resolvePromise)); - let suspend2 = false; - let resolve2; - let promise2 = new Promise(resolvePromise => (resolve2 = resolvePromise)); - - function First({text}) { - if (suspend1) { - throw promise1; - } else { - return 'Hello'; + if (__EXPERIMENTAL__) { + it('blocks only on the last continuous event (Responder system)', async () => { + useHover = require('react-interactions/events/hover').useHover; + + let suspend1 = false; + let resolve1; + let promise1 = new Promise(resolvePromise => (resolve1 = resolvePromise)); + let suspend2 = false; + let resolve2; + let promise2 = new Promise(resolvePromise => (resolve2 = resolvePromise)); + + function First({text}) { + if (suspend1) { + throw promise1; + } else { + return 'Hello'; + } } - } - function Second({text}) { - if (suspend2) { - throw promise2; - } else { - return 'World'; + function Second({text}) { + if (suspend2) { + throw promise2; + } else { + return 'World'; + } } - } - - let ops = []; - function App() { - const listener1 = useHover({ - onHoverStart() { - ops.push('Hover Start First'); - }, - onHoverEnd() { - ops.push('Hover End First'); - }, - }); - const listener2 = useHover({ - onHoverStart() { - ops.push('Hover Start Second'); - }, - onHoverEnd() { - ops.push('Hover End Second'); - }, - }); - return ( -
- - - {/* We suspend after to test what happens when we eager + let ops = []; + + function App() { + const listener1 = useHover({ + onHoverStart() { + ops.push('Hover Start First'); + }, + onHoverEnd() { + ops.push('Hover End First'); + }, + }); + const listener2 = useHover({ + onHoverStart() { + ops.push('Hover Start Second'); + }, + onHoverEnd() { + ops.push('Hover End Second'); + }, + }); + return ( +
+ + + {/* We suspend after to test what happens when we eager attach the listener. */} - - - - - - - -
- ); - } + +
+ + + + + +
+ ); + } - let finalHTML = ReactDOMServer.renderToString(); - let container = document.createElement('div'); - container.innerHTML = finalHTML; + let finalHTML = ReactDOMServer.renderToString(); + let container = document.createElement('div'); + container.innerHTML = finalHTML; - // We need this to be in the document since we'll dispatch events on it. - document.body.appendChild(container); + // We need this to be in the document since we'll dispatch events on it. + document.body.appendChild(container); - let appDiv = container.getElementsByTagName('div')[0]; - let firstSpan = appDiv.getElementsByTagName('span')[0]; - let secondSpan = appDiv.getElementsByTagName('span')[1]; - expect(firstSpan.textContent).toBe(''); - expect(secondSpan.textContent).toBe('World'); + let appDiv = container.getElementsByTagName('div')[0]; + let firstSpan = appDiv.getElementsByTagName('span')[0]; + let secondSpan = appDiv.getElementsByTagName('span')[1]; + expect(firstSpan.textContent).toBe(''); + expect(secondSpan.textContent).toBe('World'); - // On the client we don't have all data yet but we want to start - // hydrating anyway. - suspend1 = true; - suspend2 = true; - let root = ReactDOM.createRoot(container, {hydrate: true}); - root.render(); + // On the client we don't have all data yet but we want to start + // hydrating anyway. + suspend1 = true; + suspend2 = true; + let root = ReactDOM.createRoot(container, {hydrate: true}); + root.render(); - Scheduler.unstable_flushAll(); - jest.runAllTimers(); + Scheduler.unstable_flushAll(); + jest.runAllTimers(); - dispatchMouseEvent(appDiv, null); - dispatchMouseEvent(firstSpan, appDiv); - dispatchMouseEvent(secondSpan, firstSpan); + dispatchMouseEvent(appDiv, null); + dispatchMouseEvent(firstSpan, appDiv); + dispatchMouseEvent(secondSpan, firstSpan); - // Neither target is yet hydrated. - expect(ops).toEqual([]); + // Neither target is yet hydrated. + expect(ops).toEqual([]); - // Resolving the second promise so that rendering can complete. - suspend2 = false; - resolve2(); - await promise2; + // Resolving the second promise so that rendering can complete. + suspend2 = false; + resolve2(); + await promise2; - Scheduler.unstable_flushAll(); - jest.runAllTimers(); + Scheduler.unstable_flushAll(); + jest.runAllTimers(); - // We've unblocked the current hover target so we should be - // able to replay it now. - expect(ops).toEqual(['Hover Start Second']); + // We've unblocked the current hover target so we should be + // able to replay it now. + expect(ops).toEqual(['Hover Start Second']); - // Resolving the first promise has no effect now. - suspend1 = false; - resolve1(); - await promise1; + // Resolving the first promise has no effect now. + suspend1 = false; + resolve1(); + await promise1; - Scheduler.unstable_flushAll(); - jest.runAllTimers(); + Scheduler.unstable_flushAll(); + jest.runAllTimers(); - expect(ops).toEqual(['Hover Start Second']); + expect(ops).toEqual(['Hover Start Second']); - document.body.removeChild(container); - }); + document.body.removeChild(container); + }); + } it('finishes normal pri work before continuing to hydrate a retry', async () => { let suspend = false; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js index 02d1dff1a9ec6..3fb9169c1dc6e 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js @@ -17,7 +17,6 @@ let ReactDOMServer; let ReactTestUtils; let Scheduler; let Suspense; -let usePress; function dispatchMouseHoverEvent(to, from) { if (!to) { @@ -106,7 +105,6 @@ describe('ReactDOMServerSelectiveHydration', () => { ReactTestUtils = require('react-dom/test-utils'); Scheduler = require('scheduler'); Suspense = React.Suspense; - usePress = require('react-interactions/events/press').usePress; }); if (!__EXPERIMENTAL__) { @@ -352,240 +350,246 @@ describe('ReactDOMServerSelectiveHydration', () => { document.body.removeChild(container); }); - it('hydrates the target boundary synchronously during a click (flare)', async () => { - function Child({text}) { - Scheduler.unstable_yieldValue(text); - const listener = usePress({ - onPress() { - Scheduler.unstable_yieldValue('Clicked ' + text); - }, - }); + if (__EXPERIMENTAL__) { + it('hydrates the target boundary synchronously during a click (flare)', async () => { + let usePress = require('react-interactions/events/press').usePress; - return {text}; - } + function Child({text}) { + Scheduler.unstable_yieldValue(text); + const listener = usePress({ + onPress() { + Scheduler.unstable_yieldValue('Clicked ' + text); + }, + }); - function App() { - Scheduler.unstable_yieldValue('App'); - return ( -
- - - - - - -
- ); - } + return {text}; + } - let finalHTML = ReactDOMServer.renderToString(); + function App() { + Scheduler.unstable_yieldValue('App'); + return ( +
+ + + + + + +
+ ); + } - expect(Scheduler).toHaveYielded(['App', 'A', 'B']); + let finalHTML = ReactDOMServer.renderToString(); - let container = document.createElement('div'); - // We need this to be in the document since we'll dispatch events on it. - document.body.appendChild(container); + expect(Scheduler).toHaveYielded(['App', 'A', 'B']); - container.innerHTML = finalHTML; + let container = document.createElement('div'); + // We need this to be in the document since we'll dispatch events on it. + document.body.appendChild(container); - let root = ReactDOM.createRoot(container, {hydrate: true}); - root.render(); + container.innerHTML = finalHTML; - // Nothing has been hydrated so far. - expect(Scheduler).toHaveYielded([]); + let root = ReactDOM.createRoot(container, {hydrate: true}); + root.render(); - let span = container.getElementsByTagName('span')[1]; + // Nothing has been hydrated so far. + expect(Scheduler).toHaveYielded([]); - let target = createEventTarget(span); + let span = container.getElementsByTagName('span')[1]; - // This should synchronously hydrate the root App and the second suspense - // boundary. - let preventDefault = jest.fn(); - target.virtualclick({preventDefault}); + let target = createEventTarget(span); - // The event should have been canceled because we called preventDefault. - expect(preventDefault).toHaveBeenCalled(); + // This should synchronously hydrate the root App and the second suspense + // boundary. + let preventDefault = jest.fn(); + target.virtualclick({preventDefault}); - // We rendered App, B and then invoked the event without rendering A. - expect(Scheduler).toHaveYielded(['App', 'B', 'Clicked B']); + // The event should have been canceled because we called preventDefault. + expect(preventDefault).toHaveBeenCalled(); - // After continuing the scheduler, we finally hydrate A. - expect(Scheduler).toFlushAndYield(['A']); + // We rendered App, B and then invoked the event without rendering A. + expect(Scheduler).toHaveYielded(['App', 'B', 'Clicked B']); - document.body.removeChild(container); - }); + // After continuing the scheduler, we finally hydrate A. + expect(Scheduler).toFlushAndYield(['A']); - it('hydrates at higher pri if sync did not work first time (flare)', async () => { - let suspend = false; - let resolve; - let promise = new Promise(resolvePromise => (resolve = resolvePromise)); + document.body.removeChild(container); + }); - function Child({text}) { - if ((text === 'A' || text === 'D') && suspend) { - throw promise; + it('hydrates at higher pri if sync did not work first time (flare)', async () => { + let usePress = require('react-interactions/events/press').usePress; + let suspend = false; + let resolve; + let promise = new Promise(resolvePromise => (resolve = resolvePromise)); + + function Child({text}) { + if ((text === 'A' || text === 'D') && suspend) { + throw promise; + } + Scheduler.unstable_yieldValue(text); + + const listener = usePress({ + onPress() { + Scheduler.unstable_yieldValue('Clicked ' + text); + }, + }); + return {text}; } - Scheduler.unstable_yieldValue(text); - const listener = usePress({ - onPress() { - Scheduler.unstable_yieldValue('Clicked ' + text); - }, - }); - return {text}; - } + function App() { + Scheduler.unstable_yieldValue('App'); + return ( +
+ + + + + + + + + + + + +
+ ); + } - function App() { - Scheduler.unstable_yieldValue('App'); - return ( -
- - - - - - - - - - - - -
- ); - } + let finalHTML = ReactDOMServer.renderToString(); - let finalHTML = ReactDOMServer.renderToString(); + expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']); - expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']); + let container = document.createElement('div'); + // We need this to be in the document since we'll dispatch events on it. + document.body.appendChild(container); - let container = document.createElement('div'); - // We need this to be in the document since we'll dispatch events on it. - document.body.appendChild(container); + container.innerHTML = finalHTML; - container.innerHTML = finalHTML; + let spanD = container.getElementsByTagName('span')[3]; - let spanD = container.getElementsByTagName('span')[3]; + suspend = true; - suspend = true; + // A and D will be suspended. We'll click on D which should take + // priority, after we unsuspend. + let root = ReactDOM.createRoot(container, {hydrate: true}); + root.render(); - // A and D will be suspended. We'll click on D which should take - // priority, after we unsuspend. - let root = ReactDOM.createRoot(container, {hydrate: true}); - root.render(); + // Nothing has been hydrated so far. + expect(Scheduler).toHaveYielded([]); - // Nothing has been hydrated so far. - expect(Scheduler).toHaveYielded([]); + // This click target cannot be hydrated yet because it's suspended. + let result = dispatchClickEvent(spanD); - // This click target cannot be hydrated yet because it's suspended. - let result = dispatchClickEvent(spanD); + expect(Scheduler).toHaveYielded(['App']); - expect(Scheduler).toHaveYielded(['App']); + expect(result).toBe(true); - expect(result).toBe(true); + // Continuing rendering will render B next. + expect(Scheduler).toFlushAndYield(['B', 'C']); - // Continuing rendering will render B next. - expect(Scheduler).toFlushAndYield(['B', 'C']); + suspend = false; + resolve(); + await promise; - suspend = false; - resolve(); - await promise; + // After the click, we should prioritize D and the Click first, + // and only after that render A and C. + expect(Scheduler).toFlushAndYield(['D', 'Clicked D', 'A']); - // After the click, we should prioritize D and the Click first, - // and only after that render A and C. - expect(Scheduler).toFlushAndYield(['D', 'Clicked D', 'A']); + document.body.removeChild(container); + }); - document.body.removeChild(container); - }); + it('hydrates at higher pri for secondary discrete events (flare)', async () => { + let usePress = require('react-interactions/events/press').usePress; + let suspend = false; + let resolve; + let promise = new Promise(resolvePromise => (resolve = resolvePromise)); - it('hydrates at higher pri for secondary discrete events (flare)', async () => { - let suspend = false; - let resolve; - let promise = new Promise(resolvePromise => (resolve = resolvePromise)); + function Child({text}) { + if ((text === 'A' || text === 'D') && suspend) { + throw promise; + } + Scheduler.unstable_yieldValue(text); - function Child({text}) { - if ((text === 'A' || text === 'D') && suspend) { - throw promise; + const listener = usePress({ + onPress() { + Scheduler.unstable_yieldValue('Clicked ' + text); + }, + }); + return {text}; } - Scheduler.unstable_yieldValue(text); - const listener = usePress({ - onPress() { - Scheduler.unstable_yieldValue('Clicked ' + text); - }, - }); - return {text}; - } + function App() { + Scheduler.unstable_yieldValue('App'); + return ( +
+ + + + + + + + + + + + +
+ ); + } - function App() { - Scheduler.unstable_yieldValue('App'); - return ( -
- - - - - - - - - - - - -
- ); - } + let finalHTML = ReactDOMServer.renderToString(); - let finalHTML = ReactDOMServer.renderToString(); + expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']); - expect(Scheduler).toHaveYielded(['App', 'A', 'B', 'C', 'D']); + let container = document.createElement('div'); + // We need this to be in the document since we'll dispatch events on it. + document.body.appendChild(container); - let container = document.createElement('div'); - // We need this to be in the document since we'll dispatch events on it. - document.body.appendChild(container); + container.innerHTML = finalHTML; - container.innerHTML = finalHTML; + let spanA = container.getElementsByTagName('span')[0]; + let spanC = container.getElementsByTagName('span')[2]; + let spanD = container.getElementsByTagName('span')[3]; - let spanA = container.getElementsByTagName('span')[0]; - let spanC = container.getElementsByTagName('span')[2]; - let spanD = container.getElementsByTagName('span')[3]; + suspend = true; - suspend = true; + // A and D will be suspended. We'll click on D which should take + // priority, after we unsuspend. + let root = ReactDOM.createRoot(container, {hydrate: true}); + root.render(); - // A and D will be suspended. We'll click on D which should take - // priority, after we unsuspend. - let root = ReactDOM.createRoot(container, {hydrate: true}); - root.render(); + // Nothing has been hydrated so far. + expect(Scheduler).toHaveYielded([]); - // Nothing has been hydrated so far. - expect(Scheduler).toHaveYielded([]); + // This click target cannot be hydrated yet because the first is Suspended. + dispatchClickEvent(spanA); + dispatchClickEvent(spanC); + dispatchClickEvent(spanD); - // This click target cannot be hydrated yet because the first is Suspended. - dispatchClickEvent(spanA); - dispatchClickEvent(spanC); - dispatchClickEvent(spanD); + expect(Scheduler).toHaveYielded(['App']); - expect(Scheduler).toHaveYielded(['App']); + suspend = false; + resolve(); + await promise; - suspend = false; - resolve(); - await promise; - - // We should prioritize hydrating A, C and D first since we clicked in - // them. Only after they're done will we hydrate B. - expect(Scheduler).toFlushAndYield([ - 'A', - 'Clicked A', - 'C', - 'Clicked C', - 'D', - 'Clicked D', - // B should render last since it wasn't clicked. - 'B', - ]); + // We should prioritize hydrating A, C and D first since we clicked in + // them. Only after they're done will we hydrate B. + expect(Scheduler).toFlushAndYield([ + 'A', + 'Clicked A', + 'C', + 'Clicked C', + 'D', + 'Clicked D', + // B should render last since it wasn't clicked. + 'B', + ]); - document.body.removeChild(container); - }); + document.body.removeChild(container); + }); + } it('hydrates the hovered targets as higher priority for continuous events', async () => { let suspend = false; diff --git a/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js b/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js index 2a540f9929d14..678f96de9f080 100644 --- a/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js +++ b/packages/react-dom/src/__tests__/renderSubtreeIntoContainer-test.js @@ -19,7 +19,7 @@ const renderSubtreeIntoContainer = require('react-dom') const ReactFeatureFlags = require('shared/ReactFeatureFlags'); // Once this flag is always true, we should delete this test file -if (ReactFeatureFlags.disableUnstableRenderSubtreeIntoContainer) { +if (__EXPERIMENTAL__) { describe('renderSubtreeIntoContainer', () => { it('empty test', () => { // Empty test to prevent "Your test suite must contain at least one test." error. diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 791fc098293a1..340cba54773e3 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -35,7 +35,6 @@ import { attemptUserBlockingHydration, attemptContinuousHydration, attemptHydrationAtCurrentPriority, - act, } from 'react-reconciler/inline.dom'; import {createPortal as createPortalImpl} from 'shared/ReactPortal'; import {canUseDOM} from 'shared/ExecutionEnvironment'; @@ -56,14 +55,7 @@ import { } from 'legacy-events/EventPropagators'; import ReactVersion from 'shared/ReactVersion'; import invariant from 'shared/invariant'; -import { - exposeConcurrentModeAPIs, - disableLegacyReactDOMAPIs, - disableUnstableCreatePortal, - disableUnstableRenderSubtreeIntoContainer, - warnUnstableRenderSubtreeIntoContainer, - isTestEnvironment, -} from 'shared/ReactFeatureFlags'; +import {warnUnstableRenderSubtreeIntoContainer} from 'shared/ReactFeatureFlags'; import { getInstanceFromNode, @@ -120,104 +112,113 @@ function createPortal( children: ReactNodeList, container: Container, key: ?string = null, -) { +): React$Portal { invariant( isValidContainer(container), 'Target container is not a DOM element.', ); // TODO: pass ReactDOM portal implementation as third argument + // $FlowFixMe The Flow type is opaque but there's no way to actually create it. return createPortalImpl(children, container, null, key); } -const ReactDOM: Object = { - createPortal, - - unstable_batchedUpdates: batchedUpdates, - - flushSync: flushSync, - - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { - // Keep in sync with ReactDOMUnstableNativeDependencies.js - // ReactTestUtils.js, and ReactTestUtilsAct.js. This is an array for better minification. - Events: [ - getInstanceFromNode, - getNodeFromInstance, - getFiberCurrentPropsFromNode, - injectEventPluginsByName, - eventNameDispatchConfigs, - accumulateTwoPhaseDispatches, - accumulateDirectDispatches, - enqueueStateRestore, - restoreStateIfNeeded, - dispatchEvent, - runEventsInBatch, - flushPassiveEffects, - IsThisRendererActing, - ], - }, - - version: ReactVersion, -}; - -if (!disableLegacyReactDOMAPIs) { - ReactDOM.findDOMNode = findDOMNode; - ReactDOM.hydrate = hydrate; - ReactDOM.render = render; - ReactDOM.unmountComponentAtNode = unmountComponentAtNode; +function scheduleHydration(target: Node) { + if (target) { + queueExplicitHydrationTarget(target); + } } -if (exposeConcurrentModeAPIs) { - ReactDOM.createRoot = createRoot; - ReactDOM.createBlockingRoot = createBlockingRoot; - - ReactDOM.unstable_discreteUpdates = discreteUpdates; - ReactDOM.unstable_flushDiscreteUpdates = flushDiscreteUpdates; - ReactDOM.unstable_flushControlled = flushControlled; - - ReactDOM.unstable_scheduleHydration = target => { - if (target) { - queueExplicitHydrationTarget(target); +function renderSubtreeIntoContainer( + parentComponent: React$Component, + element: React$Element, + containerNode: Container, + callback: ?Function, +) { + if (__DEV__) { + if ( + warnUnstableRenderSubtreeIntoContainer && + !didWarnAboutUnstableRenderSubtreeIntoContainer + ) { + didWarnAboutUnstableRenderSubtreeIntoContainer = true; + console.warn( + 'ReactDOM.unstable_renderSubtreeIntoContainer() is deprecated ' + + 'and will be removed in a future major release. Consider using ' + + 'React Portals instead.', + ); } - }; + } + return unstable_renderSubtreeIntoContainer( + parentComponent, + element, + containerNode, + callback, + ); } -if (!disableUnstableRenderSubtreeIntoContainer) { - ReactDOM.unstable_renderSubtreeIntoContainer = function(...args) { - if (__DEV__) { - if ( - warnUnstableRenderSubtreeIntoContainer && - !didWarnAboutUnstableRenderSubtreeIntoContainer - ) { - didWarnAboutUnstableRenderSubtreeIntoContainer = true; - console.warn( - 'ReactDOM.unstable_renderSubtreeIntoContainer() is deprecated ' + - 'and will be removed in a future major release. Consider using ' + - 'React Portals instead.', - ); - } +function unstable_createPortal( + children: ReactNodeList, + container: Container, + key: ?string = null, +) { + if (__DEV__) { + if (!didWarnAboutUnstableCreatePortal) { + didWarnAboutUnstableCreatePortal = true; + console.warn( + 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' + + 'and will be removed in React 17+. Update your code to use ' + + 'ReactDOM.createPortal() instead. It has the exact same API, ' + + 'but without the "unstable_" prefix.', + ); } - return unstable_renderSubtreeIntoContainer(...args); - }; + } + return createPortal(children, container, key); } -if (!disableUnstableCreatePortal) { +const Internals = { + // Keep in sync with ReactDOMUnstableNativeDependencies.js + // ReactTestUtils.js, and ReactTestUtilsAct.js. This is an array for better minification. + Events: [ + getInstanceFromNode, + getNodeFromInstance, + getFiberCurrentPropsFromNode, + injectEventPluginsByName, + eventNameDispatchConfigs, + accumulateTwoPhaseDispatches, + accumulateDirectDispatches, + enqueueStateRestore, + restoreStateIfNeeded, + dispatchEvent, + runEventsInBatch, + flushPassiveEffects, + IsThisRendererActing, + ], +}; + +export { + createPortal, + batchedUpdates as unstable_batchedUpdates, + flushSync, + Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + ReactVersion as version, + // Disabled behind disableLegacyReactDOMAPIs + findDOMNode, + hydrate, + render, + unmountComponentAtNode, + // exposeConcurrentModeAPIs + createRoot, + createBlockingRoot, + discreteUpdates as unstable_discreteUpdates, + flushDiscreteUpdates as unstable_flushDiscreteUpdates, + flushControlled as unstable_flushControlled, + scheduleHydration as unstable_scheduleHydration, + // Disabled behind disableUnstableRenderSubtreeIntoContainer + renderSubtreeIntoContainer as unstable_renderSubtreeIntoContainer, + // Disabled behind disableUnstableCreatePortal // Temporary alias since we already shipped React 16 RC with it. // TODO: remove in React 17. - ReactDOM.unstable_createPortal = function(...args) { - if (__DEV__) { - if (!didWarnAboutUnstableCreatePortal) { - didWarnAboutUnstableCreatePortal = true; - console.warn( - 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' + - 'and will be removed in React 17+. Update your code to use ' + - 'ReactDOM.createPortal() instead. It has the exact same API, ' + - 'but without the "unstable_" prefix.', - ); - } - } - return createPortal(...args); - }; -} + unstable_createPortal, +}; const foundDevTools = injectIntoDevTools({ findFiberByHostInstance: getClosestInstanceFromNode, @@ -252,9 +253,3 @@ if (__DEV__) { } } } - -if (isTestEnvironment) { - ReactDOM.act = act; -} - -export default ReactDOM; diff --git a/packages/react-dom/src/client/ReactDOMFB.js b/packages/react-dom/src/client/ReactDOMFB.js deleted file mode 100644 index 02403919e77dd..0000000000000 --- a/packages/react-dom/src/client/ReactDOMFB.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @flow - */ - -import {addUserTimingListener} from 'shared/ReactFeatureFlags'; - -import ReactDOM from './ReactDOM'; -import {isEnabled} from '../events/ReactDOMEventListener'; -import {getClosestInstanceFromNode} from './ReactDOMComponentTree'; - -if (__EXPERIMENTAL__) { - // This is a modern WWW build. - // It should be the same as open source. Don't add new things here. -} else { - // For classic WWW builds, include a few internals that are already in use. - Object.assign( - (ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: any), - { - ReactBrowserEventEmitter: { - isEnabled, - }, - ReactDOMComponentTree: { - getClosestInstanceFromNode, - }, - // Perf experiment - addUserTimingListener, - }, - ); -} - -export default ReactDOM; diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js index f15805cee7470..c61aa20c85836 100644 --- a/packages/react-dom/src/client/ReactDOMHostConfig.js +++ b/packages/react-dom/src/client/ReactDOMHostConfig.js @@ -101,8 +101,8 @@ export type EventTargetChildElement = { ... }; export type Container = - | (Element & {_reactRootContainer: ?RootType, ...}) - | (Document & {_reactRootContainer: ?RootType, ...}); + | (Element & {_reactRootContainer?: RootType, ...}) + | (Document & {_reactRootContainer?: RootType, ...}); export type Instance = Element; export type TextInstance = Text; export type SuspenseInstance = Comment & {_reactRetry?: () => void, ...}; diff --git a/packages/react-dom/src/client/ReactDOMLegacy.js b/packages/react-dom/src/client/ReactDOMLegacy.js index d074ecf577ec6..97fbac293578a 100644 --- a/packages/react-dom/src/client/ReactDOMLegacy.js +++ b/packages/react-dom/src/client/ReactDOMLegacy.js @@ -370,6 +370,7 @@ export function unmountComponentAtNode(container: Container) { // Unmount should not be batched. unbatchedUpdates(() => { legacyRenderSubtreeIntoContainer(null, null, container, false, () => { + // $FlowFixMe This should probably use `delete container._reactRootContainer` container._reactRootContainer = null; unmarkContainerAsRoot(container); }); diff --git a/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js index 3711cbe30365b..c3fa398e83766 100644 --- a/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js +++ b/packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js @@ -66,6 +66,11 @@ function dispatchClickEvent(element) { describe('DOMEventResponderSystem', () => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { jest.resetModules(); ReactFeatureFlags = require('shared/ReactFeatureFlags'); diff --git a/packages/react-dom/testing.classic.fb.js b/packages/react-dom/testing.classic.fb.js new file mode 100644 index 0000000000000..841f6a1d8f8db --- /dev/null +++ b/packages/react-dom/testing.classic.fb.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export * from './index.classic.fb.js'; +export {act} from 'react-reconciler/inline.dom'; diff --git a/packages/react-dom/testing.experimental.js b/packages/react-dom/testing.experimental.js new file mode 100644 index 0000000000000..0727208336ccd --- /dev/null +++ b/packages/react-dom/testing.experimental.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export * from './index.experimental.js'; +export {act} from 'react-reconciler/inline.dom'; diff --git a/packages/react-dom/testing.js b/packages/react-dom/testing.js index 2a016ba16e9db..6169f8727267a 100644 --- a/packages/react-dom/testing.js +++ b/packages/react-dom/testing.js @@ -7,10 +7,5 @@ * @flow */ -'use strict'; - -const ReactDOM = require('./src/client/ReactDOM'); - -// TODO: decide on the top-level export form. -// This is hacky but makes it work with both Rollup and Jest. -module.exports = ReactDOM.default || ReactDOM; +export * from './index.js'; +export {act} from 'react-reconciler/inline.dom'; diff --git a/packages/react-dom/testing.modern.fb.js b/packages/react-dom/testing.modern.fb.js new file mode 100644 index 0000000000000..4eca948d49902 --- /dev/null +++ b/packages/react-dom/testing.modern.fb.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export * from './index.modern.fb.js'; +export {act} from 'react-reconciler/inline.dom'; diff --git a/packages/react-dom/testing.stable.js b/packages/react-dom/testing.stable.js new file mode 100644 index 0000000000000..474972b70d816 --- /dev/null +++ b/packages/react-dom/testing.stable.js @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export * from './index.stable.js'; +export {act} from 'react-reconciler/inline.dom'; diff --git a/packages/react-interactions/events/src/dom/__tests__/ContextMenu-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/ContextMenu-test.internal.js index f57d416459776..affcf8e6fbfa5 100644 --- a/packages/react-interactions/events/src/dom/__tests__/ContextMenu-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/ContextMenu-test.internal.js @@ -38,6 +38,11 @@ const table = [[forcePointerEvents], [!forcePointerEvents]]; describe.each(table)('ContextMenu responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(hasPointerEvents); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/Focus-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/Focus-test.internal.js index 66946f07abcce..0b751d5c063dc 100644 --- a/packages/react-interactions/events/src/dom/__tests__/Focus-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/Focus-test.internal.js @@ -38,6 +38,11 @@ const table = [[forcePointerEvents], [!forcePointerEvents]]; describe.each(table)('Focus responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(hasPointerEvents); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js index 647527e4e4417..6d474b429bd2e 100644 --- a/packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/FocusWithin-test.internal.js @@ -37,6 +37,11 @@ const table = [[forcePointerEvents], [!forcePointerEvents]]; describe.each(table)('FocusWithin responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/Hover-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/Hover-test.internal.js index 06437852b45c9..35f71ebabafee 100644 --- a/packages/react-interactions/events/src/dom/__tests__/Hover-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/Hover-test.internal.js @@ -34,6 +34,11 @@ const table = [[forcePointerEvents], [!forcePointerEvents]]; describe.each(table)('Hover responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(hasPointerEvents); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/Input-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/Input-test.internal.js index cf1bb6670f57d..5248e58a067f0 100644 --- a/packages/react-interactions/events/src/dom/__tests__/Input-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/Input-test.internal.js @@ -45,6 +45,11 @@ const modulesInit = () => { describe('Input event responder', () => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { jest.resetModules(); modulesInit(); diff --git a/packages/react-interactions/events/src/dom/__tests__/Keyboard-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/Keyboard-test.internal.js index 68a69dfba6c52..bd649765d5e60 100644 --- a/packages/react-interactions/events/src/dom/__tests__/Keyboard-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/Keyboard-test.internal.js @@ -28,6 +28,11 @@ function initializeModules(hasPointerEvents) { describe('Keyboard responder', () => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/MixedResponders-test-internal.js b/packages/react-interactions/events/src/dom/__tests__/MixedResponders-test-internal.js index aeb265329ed27..6dc3ee24ec7f6 100644 --- a/packages/react-interactions/events/src/dom/__tests__/MixedResponders-test-internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/MixedResponders-test-internal.js @@ -19,6 +19,11 @@ let Scheduler; describe('mixing responders with the heritage event system', () => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { ReactFeatureFlags = require('shared/ReactFeatureFlags'); ReactFeatureFlags.enableDeprecatedFlareAPI = true; diff --git a/packages/react-interactions/events/src/dom/__tests__/Press-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/Press-test.internal.js index 6987280090aae..e9245fbb53bac 100644 --- a/packages/react-interactions/events/src/dom/__tests__/Press-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/Press-test.internal.js @@ -38,6 +38,11 @@ const pointerTypesTable = [['mouse'], ['touch']]; describeWithPointerEvent('Press responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(hasPointerEvents); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js index 65cb1141f8b11..381fed4a7fad1 100644 --- a/packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js @@ -50,6 +50,11 @@ const pointerTypesTable = [['mouse'], ['touch']]; describe.each(environmentTable)('Press responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(hasPointerEvents); container = document.createElement('div'); diff --git a/packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js b/packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js index 39fd5d06f9beb..17c550a24836e 100644 --- a/packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js +++ b/packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js @@ -73,6 +73,11 @@ function tapAndReleaseOutside({ describeWithPointerEvent('Tap responder', hasPointerEvents => { let container; + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + beforeEach(() => { initializeModules(hasPointerEvents); container = document.createElement('div'); diff --git a/packages/react-is/src/__tests__/ReactIs-test.js b/packages/react-is/src/__tests__/ReactIs-test.js index c14ecaf51e1ee..a56c9f756a1d3 100644 --- a/packages/react-is/src/__tests__/ReactIs-test.js +++ b/packages/react-is/src/__tests__/ReactIs-test.js @@ -13,8 +13,6 @@ let React; let ReactDOM; let ReactIs; -const ReactFeatureFlags = require('shared/ReactFeatureFlags'); - describe('ReactIs', () => { beforeEach(() => { jest.resetModules(); @@ -56,7 +54,7 @@ describe('ReactIs', () => { expect(ReactIs.isValidElementType(MemoComponent)).toEqual(true); expect(ReactIs.isValidElementType(Context.Provider)).toEqual(true); expect(ReactIs.isValidElementType(Context.Consumer)).toEqual(true); - if (!ReactFeatureFlags.disableCreateFactory) { + if (!__EXPERIMENTAL__) { let factory; expect(() => { factory = React.createFactory('div'); diff --git a/packages/react-native-renderer/src/ReactFabric.js b/packages/react-native-renderer/src/ReactFabric.js index 13ae10b1f6276..013a4ec77ea4e 100644 --- a/packages/react-native-renderer/src/ReactFabric.js +++ b/packages/react-native-renderer/src/ReactFabric.js @@ -9,6 +9,7 @@ import type {ReactFabricType, HostComponent} from './ReactNativeTypes'; import type {ReactNodeList} from 'shared/ReactTypes'; +import type {ElementRef} from 'react'; import './ReactFabricInjection'; @@ -43,7 +44,7 @@ const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; function findHostInstance_DEPRECATED( componentOrHandle: any, -): ?React$ElementRef> { +): ?ElementRef> { if (__DEV__) { const owner = ReactCurrentOwner.current; if (owner !== null && owner.stateNode !== null) { diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js index 6e371f9ff8cd0..4f1b140f8f809 100644 --- a/packages/react-native-renderer/src/ReactFabricHostConfig.js +++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js @@ -7,7 +7,9 @@ * @flow */ +import type {ElementRef} from 'react'; import type { + HostComponent, MeasureInWindowOnSuccessCallback, MeasureLayoutOnSuccessCallback, MeasureOnSuccessCallback, @@ -126,7 +128,7 @@ class ReactFabricHostComponent { } measureLayout( - relativeToNativeNode: number | ReactFabricHostComponent, + relativeToNativeNode: number | ElementRef>, onSuccess: MeasureLayoutOnSuccessCallback, onFail?: () => void /* currently unused */, ) { diff --git a/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js b/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js index c4c74462a4d7e..f695971f4db49 100644 --- a/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js +++ b/packages/react-native-renderer/src/ReactNativeFiberHostComponent.js @@ -7,7 +7,9 @@ * @flow */ +import type {ElementRef} from 'react'; import type { + HostComponent, MeasureInWindowOnSuccessCallback, MeasureLayoutOnSuccessCallback, MeasureOnSuccessCallback, @@ -62,7 +64,7 @@ class ReactNativeFiberHostComponent { } measureLayout( - relativeToNativeNode: number | ReactNativeFiberHostComponent, + relativeToNativeNode: number | ElementRef>, onSuccess: MeasureLayoutOnSuccessCallback, onFail?: () => void /* currently unused */, ) { @@ -71,8 +73,11 @@ class ReactNativeFiberHostComponent { if (typeof relativeToNativeNode === 'number') { // Already a node handle relativeNode = relativeToNativeNode; - } else if (relativeToNativeNode._nativeTag) { - relativeNode = relativeToNativeNode._nativeTag; + } else { + let nativeNode: ReactNativeFiberHostComponent = (relativeToNativeNode: any); + if (nativeNode._nativeTag) { + relativeNode = nativeNode._nativeTag; + } } if (relativeNode == null) { diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js index e91c16a4909dc..0349591a99350 100644 --- a/packages/react-native-renderer/src/ReactNativeHostConfig.js +++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js @@ -7,8 +7,6 @@ * @flow */ -import type {ReactNativeBaseComponentViewConfig} from './ReactNativeTypes'; - import invariant from 'shared/invariant'; // Modules provided by RN: @@ -31,12 +29,7 @@ const {get: getViewConfigForType} = ReactNativeViewConfigRegistry; export type Type = string; export type Props = Object; export type Container = number; -export type Instance = { - _children: Array, - _nativeTag: number, - viewConfig: ReactNativeBaseComponentViewConfig<>, - ... -}; +export type Instance = ReactNativeFiberHostComponent; export type TextInstance = number; export type HydratableInstance = Instance | TextInstance; export type PublicInstance = Instance; diff --git a/packages/react-native-renderer/src/ReactNativeTypes.js b/packages/react-native-renderer/src/ReactNativeTypes.js index 7619625b1371f..48f89b7d858b3 100644 --- a/packages/react-native-renderer/src/ReactNativeTypes.js +++ b/packages/react-native-renderer/src/ReactNativeTypes.js @@ -126,7 +126,9 @@ export type ReactNativeType = { }; export type ReactFabricType = { - findHostInstance_DEPRECATED(componentOrHandle: any): ?HostComponent, + findHostInstance_DEPRECATED( + componentOrHandle: any, + ): ?ElementRef>, findNodeHandle(componentOrHandle: any): ?number, dispatchCommand(handle: any, command: string, args: Array): void, render( diff --git a/packages/react-reconciler/src/__tests__/ReactFiberFundamental-test.internal.js b/packages/react-reconciler/src/__tests__/ReactFiberFundamental-test.internal.js index 00b00b533b95f..081b0be2d38fa 100644 --- a/packages/react-reconciler/src/__tests__/ReactFiberFundamental-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactFiberFundamental-test.internal.js @@ -50,6 +50,11 @@ function initReactDOMServer() { } describe('ReactFiberFundamental', () => { + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + describe('NoopRenderer', () => { beforeEach(() => { initNoopRenderer(); diff --git a/packages/react-reconciler/src/__tests__/ReactScope-test.internal.js b/packages/react-reconciler/src/__tests__/ReactScope-test.internal.js index 8144a11be827c..78b863e995166 100644 --- a/packages/react-reconciler/src/__tests__/ReactScope-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactScope-test.internal.js @@ -23,6 +23,11 @@ describe('ReactScope', () => { React = require('react'); }); + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + describe('ReactDOM', () => { let ReactDOM; let container; diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js new file mode 100644 index 0000000000000..e0d720efe846d --- /dev/null +++ b/packages/react/index.classic.fb.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + Children, + createRef, + Component, + PureComponent, + createContext, + forwardRef, + lazy, + memo, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useDebugValue, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState, + Fragment, + Profiler, + StrictMode, + Suspense, + createElement, + cloneElement, + isValidElement, + version, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + createFactory, + // exposeConcurrentModeAPIs + useTransition, + useDeferredValue, + SuspenseList, + unstable_withSuspenseConfig, + // enableBlocksAPI + block, + // enableDeprecatedFlareAPI + DEPRECATED_useResponder, + DEPRECATED_createResponder, + // enableScopeAPI + unstable_createScope, + // enableJSXTransformAPI + jsx, + jsxs, + jsxDEV, +} from './src/React'; diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js new file mode 100644 index 0000000000000..e348f332b7f76 --- /dev/null +++ b/packages/react/index.experimental.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + Children, + createRef, + Component, + PureComponent, + createContext, + forwardRef, + lazy, + memo, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useDebugValue, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState, + Fragment, + Profiler, + StrictMode, + Suspense, + createElement, + cloneElement, + isValidElement, + version, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + createFactory, + // exposeConcurrentModeAPIs + useTransition, + useDeferredValue, + SuspenseList, + unstable_withSuspenseConfig, + // enableBlocksAPI + block, +} from './src/React'; diff --git a/packages/react/index.js b/packages/react/index.js index 4268898c088b9..c0dd6bbdb3c35 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -7,10 +7,74 @@ * @flow */ -'use strict'; +// Keep in sync with https://github.com/facebook/flow/blob/master/lib/react.js +export type StatelessFunctionalComponent< + P, +> = React$StatelessFunctionalComponent

; +export type ComponentType<-P> = React$ComponentType

; +export type AbstractComponent< + -Config, + +Instance = mixed, +> = React$AbstractComponent; +export type ElementType = React$ElementType; +export type Element<+C> = React$Element; +export type Key = React$Key; +export type Ref = React$Ref; +export type Node = React$Node; +export type Context = React$Context; +export type Portal = React$Portal; +export type ElementProps = React$ElementProps; +export type ElementConfig = React$ElementConfig; +export type ElementRef = React$ElementRef; +export type Config = React$Config; +export type ChildrenArray<+T> = $ReadOnlyArray> | T; +export type Interaction = { + name: string, + timestamp: number, + ... +}; -const React = require('./src/React'); - -// TODO: decide on the top-level export form. -// This is hacky but makes it work with both Rollup and Jest. -module.exports = React.default || React; +// Export all exports so that they're available in tests. +// We can't use export * from in Flow for some reason. +export { + Children, + createRef, + Component, + PureComponent, + createContext, + forwardRef, + lazy, + memo, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useDebugValue, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState, + Fragment, + Profiler, + StrictMode, + Suspense, + createElement, + cloneElement, + isValidElement, + version, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + createFactory, + useTransition, + useDeferredValue, + SuspenseList, + unstable_withSuspenseConfig, + block, + DEPRECATED_useResponder, + DEPRECATED_createResponder, + unstable_createFundamental, + unstable_createScope, + jsx, + jsxs, + jsxDEV, +} from './src/React'; diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js new file mode 100644 index 0000000000000..0c6ba986e2e08 --- /dev/null +++ b/packages/react/index.modern.fb.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + Children, + createRef, + Component, + PureComponent, + createContext, + forwardRef, + lazy, + memo, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useDebugValue, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState, + Fragment, + Profiler, + StrictMode, + Suspense, + createElement, + cloneElement, + isValidElement, + version, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + // exposeConcurrentModeAPIs + useTransition, + useDeferredValue, + SuspenseList, + unstable_withSuspenseConfig, + // enableBlocksAPI + block, + // enableDeprecatedFlareAPI + DEPRECATED_useResponder, + DEPRECATED_createResponder, + // enableScopeAPI + unstable_createScope, + // enableJSXTransformAPI + jsx, + jsxs, + jsxDEV, +} from './src/React'; diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js new file mode 100644 index 0000000000000..67a221ca674b7 --- /dev/null +++ b/packages/react/index.stable.js @@ -0,0 +1,39 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +export { + Children, + createRef, + Component, + PureComponent, + createContext, + forwardRef, + lazy, + memo, + useCallback, + useContext, + useEffect, + useImperativeHandle, + useDebugValue, + useLayoutEffect, + useMemo, + useReducer, + useRef, + useState, + Fragment, + Profiler, + StrictMode, + Suspense, + createElement, + cloneElement, + isValidElement, + version, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + createFactory, +} from './src/React'; diff --git a/packages/react/src/React.js b/packages/react/src/React.js index 23fb0f87917b6..3fd7304f99398 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/React.js @@ -18,11 +18,11 @@ import {Component, PureComponent} from './ReactBaseClasses'; import {createRef} from './ReactCreateRef'; import {forEach, map, count, toArray, only} from './ReactChildren'; import { - createElement, - createFactory, - cloneElement, + createElement as createElementProd, + createFactory as createFactoryProd, + cloneElement as cloneElementProd, isValidElement, - jsx, + jsx as jsxProd, } from './ReactElement'; import {createContext} from './ReactContext'; import {lazy} from './ReactLazy'; @@ -57,33 +57,35 @@ import ReactSharedInternals from './ReactSharedInternals'; import createFundamental from 'shared/createFundamentalComponent'; import createResponder from 'shared/createEventResponder'; import createScope from 'shared/createScope'; -import { - enableJSXTransformAPI, - enableDeprecatedFlareAPI, - enableFundamentalAPI, - enableScopeAPI, - exposeConcurrentModeAPIs, - enableBlocksAPI, - disableCreateFactory, -} from 'shared/ReactFeatureFlags'; -const React = { - Children: { - map, - forEach, - count, - toArray, - only, - }, +// TODO: Move this branching into the other module instead and just re-export. +const createElement = __DEV__ ? createElementWithValidation : createElementProd; +const cloneElement = __DEV__ ? cloneElementWithValidation : cloneElementProd; +const createFactory = __DEV__ ? createFactoryWithValidation : createFactoryProd; + +const jsxDEV = __DEV__ ? jsxWithValidation : undefined; +const jsx = __DEV__ ? jsxWithValidationDynamic : jsxProd; +// we may want to special case jsxs internally to take advantage of static children. +// for now we can ship identical prod functions +const jsxs = __DEV__ ? jsxWithValidationStatic : jsxProd; + +const Children = { + map, + forEach, + count, + toArray, + only, +}; + +export { + Children, createRef, Component, PureComponent, - createContext, forwardRef, lazy, memo, - useCallback, useContext, useEffect, @@ -94,65 +96,34 @@ const React = { useReducer, useRef, useState, - - Fragment: REACT_FRAGMENT_TYPE, - Profiler: REACT_PROFILER_TYPE, - StrictMode: REACT_STRICT_MODE_TYPE, - Suspense: REACT_SUSPENSE_TYPE, - - createElement: __DEV__ ? createElementWithValidation : createElement, - cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement, - isValidElement: isValidElement, - - version: ReactVersion, - - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals, + REACT_FRAGMENT_TYPE as Fragment, + REACT_PROFILER_TYPE as Profiler, + REACT_STRICT_MODE_TYPE as StrictMode, + REACT_SUSPENSE_TYPE as Suspense, + createElement, + cloneElement, + isValidElement, + ReactVersion as version, + ReactSharedInternals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, + // Deprecated behind disableCreateFactory + createFactory, + // Concurrent Mode + useTransition, + useDeferredValue, + REACT_SUSPENSE_LIST_TYPE as SuspenseList, + withSuspenseConfig as unstable_withSuspenseConfig, + // enableBlocksAPI + block, + // enableDeprecatedFlareAPI + useResponder as DEPRECATED_useResponder, + createResponder as DEPRECATED_createResponder, + // enableFundamentalAPI + createFundamental as unstable_createFundamental, + // enableScopeAPI + createScope as unstable_createScope, + // enableJSXTransformAPI + jsx, + jsxs, + // TODO: jsxDEV should not be exposed as a name. We might want to move it to a different entry point. + jsxDEV, }; - -if (!disableCreateFactory) { - React.createFactory = __DEV__ ? createFactoryWithValidation : createFactory; -} - -if (exposeConcurrentModeAPIs) { - React.useTransition = useTransition; - React.useDeferredValue = useDeferredValue; - React.SuspenseList = REACT_SUSPENSE_LIST_TYPE; - React.unstable_withSuspenseConfig = withSuspenseConfig; -} - -if (enableBlocksAPI) { - React.block = block; -} - -if (enableDeprecatedFlareAPI) { - React.DEPRECATED_useResponder = useResponder; - React.DEPRECATED_createResponder = createResponder; -} - -if (enableFundamentalAPI) { - React.unstable_createFundamental = createFundamental; -} - -if (enableScopeAPI) { - React.unstable_createScope = createScope; -} - -// Note: some APIs are added with feature flags. -// Make sure that stable builds for open source -// don't modify the React object to avoid deopts. -// Also let's not expose their names in stable builds. - -if (enableJSXTransformAPI) { - if (__DEV__) { - React.jsxDEV = jsxWithValidation; - React.jsx = jsxWithValidationDynamic; - React.jsxs = jsxWithValidationStatic; - } else { - React.jsx = jsx; - // we may want to special case jsxs internally to take advantage of static children. - // for now we can ship identical prod functions - React.jsxs = jsx; - } -} - -export default React; diff --git a/packages/react/src/__tests__/ReactElement-test.js b/packages/react/src/__tests__/ReactElement-test.js index af99767220a1c..6c3d4f6cebae0 100644 --- a/packages/react/src/__tests__/ReactElement-test.js +++ b/packages/react/src/__tests__/ReactElement-test.js @@ -13,8 +13,6 @@ let React; let ReactDOM; let ReactTestUtils; -const ReactFeatureFlags = require('shared/ReactFeatureFlags'); - describe('ReactElement', () => { let ComponentClass; let originalSymbol; @@ -312,7 +310,7 @@ describe('ReactElement', () => { expect(React.isValidElement(true)).toEqual(false); expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); - if (!ReactFeatureFlags.disableCreateFactory) { + if (!__EXPERIMENTAL__) { let factory; expect(() => { factory = React.createFactory('div'); @@ -481,7 +479,7 @@ describe('ReactElement', () => { expect(React.isValidElement(true)).toEqual(false); expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); - if (!ReactFeatureFlags.disableCreateFactory) { + if (!__EXPERIMENTAL__) { let factory; expect(() => { factory = React.createFactory('div'); diff --git a/packages/react/src/__tests__/ReactElementJSX-test.internal.js b/packages/react/src/__tests__/ReactElementJSX-test.internal.js index cb45ed714b78f..4a02bbc1a379d 100644 --- a/packages/react/src/__tests__/ReactElementJSX-test.internal.js +++ b/packages/react/src/__tests__/ReactElementJSX-test.internal.js @@ -31,7 +31,6 @@ describe('ReactElement.jsx', () => { global.Symbol = undefined; ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableJSXTransformAPI = true; ReactFeatureFlags.warnAboutSpreadingKeyToJSX = true; React = require('react'); @@ -43,6 +42,11 @@ describe('ReactElement.jsx', () => { global.Symbol = originalSymbol; }); + if (!__EXPERIMENTAL__) { + it("empty test so Jest doesn't complain", () => {}); + return; + } + it('allows static methods to be called using the type property', () => { class StaticMethodComponentClass extends React.Component { render() { @@ -69,7 +73,7 @@ describe('ReactElement.jsx', () => { expect(React.isValidElement(true)).toEqual(false); expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); - if (!ReactFeatureFlags.disableCreateFactory) { + if (!__EXPERIMENTAL__) { let factory; expect(() => { factory = React.createFactory('div'); @@ -292,9 +296,6 @@ describe('ReactElement.jsx', () => { jest.resetModules(); - ReactFeatureFlags = require('shared/ReactFeatureFlags'); - ReactFeatureFlags.enableJSXTransformAPI = true; - React = require('react'); class Component extends React.Component { @@ -310,7 +311,7 @@ describe('ReactElement.jsx', () => { expect(React.isValidElement(true)).toEqual(false); expect(React.isValidElement({})).toEqual(false); expect(React.isValidElement('string')).toEqual(false); - if (!ReactFeatureFlags.disableCreateFactory) { + if (!__EXPERIMENTAL__) { let factory; expect(() => { factory = React.createFactory('div'); diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js index c4016387e159e..9380998460c11 100644 --- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js +++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js @@ -439,7 +439,7 @@ describe('ReactElementValidator', () => { ); }); - if (!ReactFeatureFlags.disableCreateFactory) { + if (!__EXPERIMENTAL__) { it('should warn when accessing .type on an element factory', () => { function TestComponent() { return

; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index eab16bb07932d..784accd974c29 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -44,10 +44,6 @@ export function addUserTimingListener() { // Disable javascript: URL strings in href for XSS protection. export const disableJavaScriptURLs = false; -// These APIs will no longer be "unstable" in the upcoming 16.7 release, -// Control this behavior with a flag to support 16.6 minor releases in the meanwhile. -export const exposeConcurrentModeAPIs = __EXPERIMENTAL__; - // Warns when a combination of updates on a dom can cause a style declaration // that clashes with a previous one https://github.com/facebook/react/pull/14181 export const warnAboutShorthandPropertyCollision = true; @@ -62,7 +58,6 @@ export const enableFundamentalAPI = false; export const enableScopeAPI = false; // New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107 -export const enableJSXTransformAPI = false; // We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?) // Till then, we warn about the missing mock, but still fallback to a legacy mode compatible version @@ -108,10 +103,6 @@ export const runAllPassiveEffectDestroysBeforeCreates = false; // WARNING This flag only has an affect if used with runAllPassiveEffectDestroysBeforeCreates. export const deferPassiveEffectCleanupDuringUnmount = false; -// Use this flag to generate "testing" builds, that include APIs like act() -// and extra warnings/errors -export const isTestEnvironment = false; - // Enables a warning when trying to spread a 'key' to an element; // a deprecated pattern we want to get rid of in the future export const warnAboutSpreadingKeyToJSX = false; @@ -128,25 +119,14 @@ export const warnAboutStringRefs = false; export const disableLegacyContext = false; -// Disables React.createFactory -export const disableCreateFactory = false; - -// Disables hydrate, render, findDOMNode, unmountComponentAtNode -export const disableLegacyReactDOMAPIs = false; - // Disables children for