diff --git a/.eslintrc.js b/.eslintrc.js index 74b87ab13a679..7d34ba704554a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -168,6 +168,7 @@ module.exports = { __PROFILE__: true, __UMD__: true, __EXPERIMENTAL__: true, + __TEST__: true, trustedTypes: true, }, }; diff --git a/fixtures/dom/src/__tests__/nested-act-test.js b/fixtures/dom/src/__tests__/nested-act-test.js index a0500f47e9603..03c2aacd1b62f 100644 --- a/fixtures/dom/src/__tests__/nested-act-test.js +++ b/fixtures/dom/src/__tests__/nested-act-test.js @@ -8,18 +8,24 @@ */ let React; -let TestUtils; +let ReactDOM; let TestRenderer; global.__DEV__ = process.env.NODE_ENV !== 'production'; +jest.mock('react', () => require.requireActual('react/cjs/react.testing.js')); +jest.mock('react-dom', () => + require.requireActual('react-dom/cjs/react-dom.testing.js') +); +// we'll replace the above with react/testing and react-dom/testing right before the next minor + expect.extend(require('../toWarnDev')); describe('unmocked scheduler', () => { beforeEach(() => { jest.resetModules(); React = require('react'); - TestUtils = require('react-dom/test-utils'); + ReactDOM = require('react-dom'); TestRenderer = require('react-test-renderer'); }); @@ -33,7 +39,7 @@ describe('unmocked scheduler', () => { } // in legacy mode, this tests whether an act only flushes its own effects TestRenderer.act(() => { - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.create(); }); expect(log).toEqual([]); @@ -42,7 +48,7 @@ describe('unmocked scheduler', () => { log = []; // for doublechecking, we flip it inside out, and assert on the outermost - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.act(() => { TestRenderer.create(); }); @@ -59,7 +65,7 @@ describe('mocked scheduler', () => { require.requireActual('scheduler/unstable_mock') ); React = require('react'); - TestUtils = require('react-dom/test-utils'); + ReactDOM = require('react-dom'); TestRenderer = require('react-test-renderer'); }); @@ -77,7 +83,7 @@ describe('mocked scheduler', () => { } // with a mocked scheduler, this tests whether it flushes all work only on the outermost act TestRenderer.act(() => { - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.create(); }); expect(log).toEqual([]); @@ -86,7 +92,7 @@ describe('mocked scheduler', () => { log = []; // for doublechecking, we flip it inside out, and assert on the outermost - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.act(() => { TestRenderer.create(); }); diff --git a/fixtures/dom/src/__tests__/wrong-act-test.js b/fixtures/dom/src/__tests__/wrong-act-test.js index 6c054efec1bf9..99f509b10abef 100644 --- a/fixtures/dom/src/__tests__/wrong-act-test.js +++ b/fixtures/dom/src/__tests__/wrong-act-test.js @@ -12,13 +12,18 @@ let ReactDOM; let ReactART; let ARTSVGMode; let ARTCurrentMode; -let TestUtils; let TestRenderer; let ARTTest; global.__DEV__ = process.env.NODE_ENV !== 'production'; global.__EXPERIMENTAL__ = process.env.RELEASE_CHANNEL === 'experimental'; +jest.mock('react', () => require.requireActual('react/cjs/react.testing.js')); +jest.mock('react-dom', () => + require.requireActual('react-dom/cjs/react-dom.testing.js') +); +// we'll replace the above with react/testing and react-dom/testing right before the next minor + expect.extend(require('../toWarnDev')); function App(props) { @@ -32,7 +37,6 @@ beforeEach(() => { ReactART = require('react-art'); ARTSVGMode = require('art/modes/svg'); ARTCurrentMode = require('art/modes/current'); - TestUtils = require('react-dom/test-utils'); TestRenderer = require('react-test-renderer'); ARTCurrentMode.setCurrent(ARTSVGMode); @@ -70,8 +74,8 @@ beforeEach(() => { }); it("doesn't warn when you use the right act + renderer: dom", () => { - TestUtils.act(() => { - TestUtils.renderIntoDocument(); + ReactDOM.act(() => { + ReactDOM.render(, document.createElement('div')); }); }); @@ -86,7 +90,7 @@ it('resets correctly across renderers', () => { React.useEffect(() => {}, []); return null; } - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.act(() => {}); expect(() => { TestRenderer.create(); @@ -99,7 +103,7 @@ it('resets correctly across renderers', () => { it('warns when using the wrong act version - test + dom: render', () => { expect(() => { TestRenderer.act(() => { - TestUtils.renderIntoDocument(); + ReactDOM.render(, document.createElement('div')); }); }).toWarnDev(["It looks like you're using the wrong act()"], { withoutStack: true, @@ -113,7 +117,7 @@ it('warns when using the wrong act version - test + dom: updates', () => { setCtr = _setCtr; return ctr; } - TestUtils.renderIntoDocument(); + ReactDOM.render(, document.createElement('div')); expect(() => { TestRenderer.act(() => { setCtr(1); @@ -123,7 +127,7 @@ it('warns when using the wrong act version - test + dom: updates', () => { it('warns when using the wrong act version - dom + test: .create()', () => { expect(() => { - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.create(); }); }).toWarnDev(["It looks like you're using the wrong act()"], { @@ -134,7 +138,7 @@ it('warns when using the wrong act version - dom + test: .create()', () => { it('warns when using the wrong act version - dom + test: .update()', () => { const root = TestRenderer.create(); expect(() => { - TestUtils.act(() => { + ReactDOM.act(() => { root.update(); }); }).toWarnDev(["It looks like you're using the wrong act()"], { @@ -151,15 +155,15 @@ it('warns when using the wrong act version - dom + test: updates', () => { } TestRenderer.create(); expect(() => { - TestUtils.act(() => { + ReactDOM.act(() => { setCtr(1); }); }).toWarnDev(["It looks like you're using the wrong act()"]); }); it('does not warn when nesting react-act inside react-dom', () => { - TestUtils.act(() => { - TestUtils.renderIntoDocument(); + ReactDOM.act(() => { + ReactDOM.render(, document.createElement('div')); }); }); @@ -171,7 +175,7 @@ it('does not warn when nesting react-act inside react-test-renderer', () => { it("doesn't warn if you use nested acts from different renderers", () => { TestRenderer.act(() => { - TestUtils.act(() => { + ReactDOM.act(() => { TestRenderer.create(); }); }); diff --git a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js index d68a4a1141709..2f14259735000 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js @@ -722,6 +722,7 @@ function runActTests(label, render, unmount, rerender) { describe('suspense', () => { if (__DEV__ && __EXPERIMENTAL__) { + // todo - remove __DEV__ check once we start using testing builds it('triggers fallbacks if available', async () => { let resolved = false; let resolve; diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 51b4071363b01..54a9419b924cd 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -35,6 +35,7 @@ import { attemptUserBlockingHydration, attemptContinuousHydration, attemptHydrationAtCurrentPriority, + act, } from 'react-reconciler/inline.dom'; import {createPortal as createPortalImpl} from 'shared/ReactPortal'; import {canUseDOM} from 'shared/ExecutionEnvironment'; @@ -249,4 +250,8 @@ if (__DEV__) { } } +if (__TEST__) { + ReactDOM.act = act; +} + export default ReactDOM; diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js index 5c11164ae66c4..87f72814ac96a 100644 --- a/packages/react-noop-renderer/src/createReactNoop.js +++ b/packages/react-noop-renderer/src/createReactNoop.js @@ -14,7 +14,6 @@ * environment. */ -import type {Thenable} from 'react-reconciler/src/ReactFiberWorkLoop'; import type {Fiber} from 'react-reconciler/src/ReactFiber'; import type {UpdateQueue} from 'react-reconciler/src/ReactUpdateQueue'; import type {ReactNodeList} from 'shared/ReactTypes'; @@ -23,8 +22,6 @@ import type {RootTag} from 'shared/ReactRootTags'; import * as Scheduler from 'scheduler/unstable_mock'; import {createPortal} from 'shared/ReactPortal'; import {REACT_FRAGMENT_TYPE, REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; -import enqueueTask from 'shared/enqueueTask'; -import ReactSharedInternals from 'shared/ReactSharedInternals'; import {ConcurrentRoot, BlockingRoot, LegacyRoot} from 'shared/ReactRootTags'; type Container = { @@ -60,8 +57,6 @@ type TextInstance = {| |}; type HostContext = Object; -const {IsSomeRendererActing} = ReactSharedInternals; - const NO_CONTEXT = {}; const UPPERCASE_CONTEXT = {}; const UPDATE_SIGNAL = {}; @@ -597,197 +592,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { const roots = new Map(); const DEFAULT_ROOT_ID = ''; - const { - flushPassiveEffects, - batchedUpdates, - IsThisRendererActing, - } = NoopRenderer; - - // this act() implementation should be exactly the same in - // ReactTestUtilsAct.js, ReactTestRendererAct.js, createReactNoop.js - - const isSchedulerMocked = - typeof Scheduler.unstable_flushAllWithoutAsserting === 'function'; - const flushWork = - Scheduler.unstable_flushAllWithoutAsserting || - function() { - let didFlushWork = false; - while (flushPassiveEffects()) { - didFlushWork = true; - } - - return didFlushWork; - }; - - function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) { - try { - flushWork(); - enqueueTask(() => { - if (flushWork()) { - flushWorkAndMicroTasks(onDone); - } else { - onDone(); - } - }); - } catch (err) { - onDone(err); - } - } - - // we track the 'depth' of the act() calls with this counter, - // so we can tell if any async act() calls try to run in parallel. - - let actingUpdatesScopeDepth = 0; - let didWarnAboutUsingActInProd = false; - - function act(callback: () => Thenable) { - if (!__DEV__) { - if (didWarnAboutUsingActInProd === false) { - didWarnAboutUsingActInProd = true; - // eslint-disable-next-line react-internal/no-production-logging - console.error( - 'act(...) is not supported in production builds of React, and might not behave as expected.', - ); - } - } - let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth; - let previousIsSomeRendererActing; - let previousIsThisRendererActing; - actingUpdatesScopeDepth++; - - previousIsSomeRendererActing = IsSomeRendererActing.current; - previousIsThisRendererActing = IsThisRendererActing.current; - IsSomeRendererActing.current = true; - IsThisRendererActing.current = true; - - function onDone() { - actingUpdatesScopeDepth--; - IsSomeRendererActing.current = previousIsSomeRendererActing; - IsThisRendererActing.current = previousIsThisRendererActing; - if (__DEV__) { - if (actingUpdatesScopeDepth > previousActingUpdatesScopeDepth) { - // if it's _less than_ previousActingUpdatesScopeDepth, then we can assume the 'other' one has warned - console.error( - 'You seem to have overlapping act() calls, this is not supported. ' + - 'Be sure to await previous act() calls before making a new one. ', - ); - } - } - } - - let result; - try { - result = batchedUpdates(callback); - } catch (error) { - // on sync errors, we still want to 'cleanup' and decrement actingUpdatesScopeDepth - onDone(); - throw error; - } - - if ( - result !== null && - typeof result === 'object' && - typeof result.then === 'function' - ) { - // setup a boolean that gets set to true only - // once this act() call is await-ed - let called = false; - if (__DEV__) { - if (typeof Promise !== 'undefined') { - //eslint-disable-next-line no-undef - Promise.resolve() - .then(() => {}) - .then(() => { - if (called === false) { - console.error( - 'You called act(async () => ...) without await. ' + - 'This could lead to unexpected testing behaviour, interleaving multiple act ' + - 'calls and mixing their scopes. You should - await act(async () => ...);', - ); - } - }); - } - } - - // in the async case, the returned thenable runs the callback, flushes - // effects and microtasks in a loop until flushPassiveEffects() === false, - // and cleans up - return { - then(resolve: () => void, reject: (?Error) => void) { - called = true; - result.then( - () => { - if ( - actingUpdatesScopeDepth > 1 || - (isSchedulerMocked === true && - previousIsSomeRendererActing === true) - ) { - onDone(); - resolve(); - return; - } - // we're about to exit the act() scope, - // now's the time to flush tasks/effects - flushWorkAndMicroTasks((err: ?Error) => { - onDone(); - if (err) { - reject(err); - } else { - resolve(); - } - }); - }, - err => { - onDone(); - reject(err); - }, - ); - }, - }; - } else { - if (__DEV__) { - if (result !== undefined) { - console.error( - 'The callback passed to act(...) function ' + - 'must return undefined, or a Promise. You returned %s', - result, - ); - } - } - - // flush effects until none remain, and cleanup - try { - if ( - actingUpdatesScopeDepth === 1 && - (isSchedulerMocked === false || - previousIsSomeRendererActing === false) - ) { - // we're about to exit the act() scope, - // now's the time to flush effects - flushWork(); - } - onDone(); - } catch (err) { - onDone(); - throw err; - } - - // in the sync case, the returned thenable only warns *if* await-ed - return { - then(resolve: () => void) { - if (__DEV__) { - console.error( - 'Do not await the result of calling act(...) with sync logic, it is not a Promise.', - ); - } - resolve(); - }, - }; - } - } - - // end act() implementation - function childToJSX(child, text) { if (text !== null) { return text; @@ -1106,7 +910,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) { flushPassiveEffects: NoopRenderer.flushPassiveEffects, - act, + act: NoopRenderer.act, // Logs the current state of the tree. dumpTree(rootID: string = DEFAULT_ROOT_ID) { diff --git a/packages/react-reconciler/src/ReactFiberReconciler.js b/packages/react-reconciler/src/ReactFiberReconciler.js index fb46524645aee..dca7de0fe737c 100644 --- a/packages/react-reconciler/src/ReactFiberReconciler.js +++ b/packages/react-reconciler/src/ReactFiberReconciler.js @@ -87,6 +87,11 @@ import { findHostInstancesForRefresh, } from './ReactFiberHotReloading'; +// used by __TEST__ builds +import enqueueTask from 'shared/enqueueTask'; +import * as Scheduler from 'scheduler'; +// end __TEST__ imports + type OpaqueRoot = FiberRoot; // 0 is PROD, 1 is DEV. @@ -538,3 +543,185 @@ export function injectIntoDevTools(devToolsConfig: DevToolsConfig): boolean { getCurrentFiber: __DEV__ ? () => ReactCurrentFiberCurrent : null, }); } + +const {IsSomeRendererActing} = ReactSharedInternals; +const isSchedulerMocked = + typeof Scheduler.unstable_flushAllWithoutAsserting === 'function'; +const flushWork = + Scheduler.unstable_flushAllWithoutAsserting || + function() { + let didFlushWork = false; + while (flushPassiveEffects()) { + didFlushWork = true; + } + + return didFlushWork; + }; + +function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) { + try { + flushWork(); + enqueueTask(() => { + if (flushWork()) { + flushWorkAndMicroTasks(onDone); + } else { + onDone(); + } + }); + } catch (err) { + onDone(err); + } +} + +// we track the 'depth' of the act() calls with this counter, +// so we can tell if any async act() calls try to run in parallel. + +let actingUpdatesScopeDepth = 0; +let didWarnAboutUsingActInProd = false; + +// eslint-disable-next-line no-inner-declarations +export function act(callback: () => Thenable) { + if (!__DEV__) { + if (didWarnAboutUsingActInProd === false) { + didWarnAboutUsingActInProd = true; + // eslint-disable-next-line react-internal/no-production-logging + console.error( + 'act(...) is not supported in production builds of React, and might not behave as expected.', + ); + } + } + + let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth; + let previousIsSomeRendererActing; + let previousIsThisRendererActing; + actingUpdatesScopeDepth++; + + previousIsSomeRendererActing = IsSomeRendererActing.current; + previousIsThisRendererActing = IsThisRendererActing.current; + IsSomeRendererActing.current = true; + IsThisRendererActing.current = true; + + function onDone() { + actingUpdatesScopeDepth--; + IsSomeRendererActing.current = previousIsSomeRendererActing; + IsThisRendererActing.current = previousIsThisRendererActing; + if (__DEV__) { + if (actingUpdatesScopeDepth > previousActingUpdatesScopeDepth) { + // if it's _less than_ previousActingUpdatesScopeDepth, then we can assume the 'other' one has warned + console.error( + 'You seem to have overlapping act() calls, this is not supported. ' + + 'Be sure to await previous act() calls before making a new one. ', + ); + } + } + } + + let result; + try { + result = batchedUpdates(callback); + } catch (error) { + // on sync errors, we still want to 'cleanup' and decrement actingUpdatesScopeDepth + onDone(); + throw error; + } + + if ( + result !== null && + typeof result === 'object' && + typeof result.then === 'function' + ) { + // setup a boolean that gets set to true only + // once this act() call is await-ed + let called = false; + if (__DEV__) { + if (typeof Promise !== 'undefined') { + //eslint-disable-next-line no-undef + Promise.resolve() + .then(() => {}) + .then(() => { + if (called === false) { + console.error( + 'You called act(async () => ...) without await. ' + + 'This could lead to unexpected testing behaviour, interleaving multiple act ' + + 'calls and mixing their scopes. You should - await act(async () => ...);', + ); + } + }); + } + } + + // in the async case, the returned thenable runs the callback, flushes + // effects and microtasks in a loop until flushPassiveEffects() === false, + // and cleans up + return { + then(resolve: () => void, reject: (?Error) => void) { + called = true; + result.then( + () => { + if ( + actingUpdatesScopeDepth > 1 || + (isSchedulerMocked === true && + previousIsSomeRendererActing === true) + ) { + onDone(); + resolve(); + return; + } + // we're about to exit the act() scope, + // now's the time to flush tasks/effects + flushWorkAndMicroTasks((err: ?Error) => { + onDone(); + if (err) { + reject(err); + } else { + resolve(); + } + }); + }, + err => { + onDone(); + reject(err); + }, + ); + }, + }; + } else { + if (__DEV__) { + if (result !== undefined) { + console.error( + 'The callback passed to act(...) function ' + + 'must return undefined, or a Promise. You returned %s', + result, + ); + } + } + + // flush effects until none remain, and cleanup + try { + if ( + actingUpdatesScopeDepth === 1 && + (isSchedulerMocked === false || previousIsSomeRendererActing === false) + ) { + // we're about to exit the act() scope, + // now's the time to flush effects + flushWork(); + } + onDone(); + } catch (err) { + onDone(); + throw err; + } + + // in the sync case, the returned thenable only warns *if* await-ed + return { + then(resolve: () => void) { + if (__DEV__) { + console.error( + 'Do not await the result of calling act(...) with sync logic, it is not a Promise.', + ); + } + resolve(); + }, + }; + } +} diff --git a/packages/react-test-renderer/src/ReactTestRenderer.js b/packages/react-test-renderer/src/ReactTestRenderer.js index ca56ffaaa2685..e8ec6cbd53e6a 100644 --- a/packages/react-test-renderer/src/ReactTestRenderer.js +++ b/packages/react-test-renderer/src/ReactTestRenderer.js @@ -19,6 +19,7 @@ import { flushSync, injectIntoDevTools, batchedUpdates, + act, } from 'react-reconciler/inline.test'; import {findCurrentFiberUsingSlowPath} from 'react-reconciler/reflection'; import { @@ -42,7 +43,6 @@ import { } from 'shared/ReactWorkTags'; import invariant from 'shared/invariant'; import ReactVersion from 'shared/ReactVersion'; -import act from './ReactTestRendererAct'; import {getPublicInstance} from './ReactTestHostConfig'; import {ConcurrentRoot, LegacyRoot} from 'shared/ReactRootTags'; diff --git a/packages/react-test-renderer/src/ReactTestRendererAct.js b/packages/react-test-renderer/src/ReactTestRendererAct.js deleted file mode 100644 index 362de4e2b22ae..0000000000000 --- a/packages/react-test-renderer/src/ReactTestRendererAct.js +++ /dev/null @@ -1,204 +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 type {Thenable} from 'react-reconciler/src/ReactFiberWorkLoop'; - -import { - batchedUpdates, - flushPassiveEffects, - IsThisRendererActing, -} from 'react-reconciler/inline.test'; -import ReactSharedInternals from 'shared/ReactSharedInternals'; -import enqueueTask from 'shared/enqueueTask'; -import * as Scheduler from 'scheduler'; - -const {IsSomeRendererActing} = ReactSharedInternals; - -// this implementation should be exactly the same in -// ReactTestUtilsAct.js, ReactTestRendererAct.js, createReactNoop.js - -const isSchedulerMocked = - typeof Scheduler.unstable_flushAllWithoutAsserting === 'function'; -const flushWork = - Scheduler.unstable_flushAllWithoutAsserting || - function() { - let didFlushWork = false; - while (flushPassiveEffects()) { - didFlushWork = true; - } - - return didFlushWork; - }; - -function flushWorkAndMicroTasks(onDone: (err: ?Error) => void) { - try { - flushWork(); - enqueueTask(() => { - if (flushWork()) { - flushWorkAndMicroTasks(onDone); - } else { - onDone(); - } - }); - } catch (err) { - onDone(err); - } -} - -// we track the 'depth' of the act() calls with this counter, -// so we can tell if any async act() calls try to run in parallel. - -let actingUpdatesScopeDepth = 0; -let didWarnAboutUsingActInProd = false; - -function act(callback: () => Thenable) { - if (!__DEV__) { - if (didWarnAboutUsingActInProd === false) { - didWarnAboutUsingActInProd = true; - // eslint-disable-next-line react-internal/no-production-logging - console.error( - 'act(...) is not supported in production builds of React, and might not behave as expected.', - ); - } - } - let previousActingUpdatesScopeDepth = actingUpdatesScopeDepth; - let previousIsSomeRendererActing; - let previousIsThisRendererActing; - actingUpdatesScopeDepth++; - - previousIsSomeRendererActing = IsSomeRendererActing.current; - previousIsThisRendererActing = IsThisRendererActing.current; - IsSomeRendererActing.current = true; - IsThisRendererActing.current = true; - - function onDone() { - actingUpdatesScopeDepth--; - IsSomeRendererActing.current = previousIsSomeRendererActing; - IsThisRendererActing.current = previousIsThisRendererActing; - if (__DEV__) { - if (actingUpdatesScopeDepth > previousActingUpdatesScopeDepth) { - // if it's _less than_ previousActingUpdatesScopeDepth, then we can assume the 'other' one has warned - console.error( - 'You seem to have overlapping act() calls, this is not supported. ' + - 'Be sure to await previous act() calls before making a new one. ', - ); - } - } - } - - let result; - try { - result = batchedUpdates(callback); - } catch (error) { - // on sync errors, we still want to 'cleanup' and decrement actingUpdatesScopeDepth - onDone(); - throw error; - } - - if ( - result !== null && - typeof result === 'object' && - typeof result.then === 'function' - ) { - // setup a boolean that gets set to true only - // once this act() call is await-ed - let called = false; - if (__DEV__) { - if (typeof Promise !== 'undefined') { - //eslint-disable-next-line no-undef - Promise.resolve() - .then(() => {}) - .then(() => { - if (called === false) { - console.error( - 'You called act(async () => ...) without await. ' + - 'This could lead to unexpected testing behaviour, interleaving multiple act ' + - 'calls and mixing their scopes. You should - await act(async () => ...);', - ); - } - }); - } - } - - // in the async case, the returned thenable runs the callback, flushes - // effects and microtasks in a loop until flushPassiveEffects() === false, - // and cleans up - return { - then(resolve: () => void, reject: (?Error) => void) { - called = true; - result.then( - () => { - if ( - actingUpdatesScopeDepth > 1 || - (isSchedulerMocked === true && - previousIsSomeRendererActing === true) - ) { - onDone(); - resolve(); - return; - } - // we're about to exit the act() scope, - // now's the time to flush tasks/effects - flushWorkAndMicroTasks((err: ?Error) => { - onDone(); - if (err) { - reject(err); - } else { - resolve(); - } - }); - }, - err => { - onDone(); - reject(err); - }, - ); - }, - }; - } else { - if (__DEV__) { - if (result !== undefined) { - console.error( - 'The callback passed to act(...) function ' + - 'must return undefined, or a Promise. You returned %s', - result, - ); - } - } - - // flush effects until none remain, and cleanup - try { - if ( - actingUpdatesScopeDepth === 1 && - (isSchedulerMocked === false || previousIsSomeRendererActing === false) - ) { - // we're about to exit the act() scope, - // now's the time to flush effects - flushWork(); - } - onDone(); - } catch (err) { - onDone(); - throw err; - } - - // in the sync case, the returned thenable only warns *if* await-ed - return { - then(resolve: () => void) { - if (__DEV__) { - console.error( - 'Do not await the result of calling act(...) with sync logic, it is not a Promise.', - ); - } - resolve(); - }, - }; - } -} - -export default act; diff --git a/packages/shared/enqueueTask.js b/packages/shared/enqueueTask.js index 5bf23c18f3a02..c6905c89696e0 100644 --- a/packages/shared/enqueueTask.js +++ b/packages/shared/enqueueTask.js @@ -8,37 +8,41 @@ */ let didWarnAboutMessageChannel = false; -let enqueueTask; -try { - // read require off the module object to get around the bundlers. - // we don't want them to detect a require and bundle a Node polyfill. - let requireString = ('require' + Math.random()).slice(0, 7); - let nodeRequire = module && module[requireString]; - // assuming we're in node, let's try to get node's - // version of setImmediate, bypassing fake timers if any. - enqueueTask = nodeRequire('timers').setImmediate; -} catch (_err) { - // we're in a browser - // we can't use regular timers because they may still be faked - // so we try MessageChannel+postMessage instead - enqueueTask = function(callback: () => void) { - if (__DEV__) { - if (didWarnAboutMessageChannel === false) { - didWarnAboutMessageChannel = true; - if (typeof MessageChannel === 'undefined') { - console.error( - 'This browser does not have a MessageChannel implementation, ' + - 'so enqueuing tasks via await act(async () => ...) will fail. ' + - 'Please file an issue at https://github.com/facebook/react/issues ' + - 'if you encounter this warning.', - ); +let enqueueTaskImpl = null; + +export default function enqueueTask(task: () => void) { + if (enqueueTaskImpl === null) { + try { + // read require off the module object to get around the bundlers. + // we don't want them to detect a require and bundle a Node polyfill. + let requireString = ('require' + Math.random()).slice(0, 7); + let nodeRequire = module && module[requireString]; + // assuming we're in node, let's try to get node's + // version of setImmediate, bypassing fake timers if any. + enqueueTaskImpl = nodeRequire('timers').setImmediate; + } catch (_err) { + // we're in a browser + // we can't use regular timers because they may still be faked + // so we try MessageChannel+postMessage instead + enqueueTaskImpl = function(callback: () => void) { + if (__DEV__) { + if (didWarnAboutMessageChannel === false) { + didWarnAboutMessageChannel = true; + if (typeof MessageChannel === 'undefined') { + console.error( + 'This browser does not have a MessageChannel implementation, ' + + 'so enqueuing tasks via await act(async () => ...) will fail. ' + + 'Please file an issue at https://github.com/facebook/react/issues ' + + 'if you encounter this warning.', + ); + } + } } - } + const channel = new MessageChannel(); + channel.port1.onmessage = callback; + channel.port2.postMessage(undefined); + }; } - const channel = new MessageChannel(); - channel.port1.onmessage = callback; - channel.port2.postMessage(undefined); - }; + } + return enqueueTaskImpl(task); } - -export default enqueueTask; diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js index 524cae905bc2e..de6b36c7027c2 100644 --- a/scripts/flow/environment.js +++ b/scripts/flow/environment.js @@ -12,6 +12,7 @@ declare var __PROFILE__: boolean; declare var __UMD__: boolean; declare var __EXPERIMENTAL__: boolean; +declare var __TEST__: boolean; declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: any; /*?{ inject: ?((stuff: Object) => void) diff --git a/scripts/jest/setupEnvironment.js b/scripts/jest/setupEnvironment.js index 7eef6432e73cf..cee6fd0d6cdd4 100644 --- a/scripts/jest/setupEnvironment.js +++ b/scripts/jest/setupEnvironment.js @@ -7,6 +7,7 @@ if (NODE_ENV !== 'development' && NODE_ENV !== 'production') { global.__DEV__ = NODE_ENV === 'development'; global.__PROFILE__ = NODE_ENV === 'development'; global.__UMD__ = false; +global.__TEST__ = true; const RELEASE_CHANNEL = process.env.RELEASE_CHANNEL; diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index ec0cc2c1754bb..b20782ec05895 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -48,12 +48,15 @@ const { UMD_DEV, UMD_PROD, UMD_PROFILING, + UMD_TESTING, NODE_DEV, NODE_PROD, NODE_PROFILING, + NODE_TESTING, FB_WWW_DEV, FB_WWW_PROD, FB_WWW_PROFILING, + FB_WWW_TESTING, RN_OSS_DEV, RN_OSS_PROD, RN_OSS_PROFILING, @@ -139,6 +142,7 @@ function getBabelConfig( case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return Object.assign({}, options, { plugins: options.plugins.concat([ // Minify invariant messages @@ -163,9 +167,11 @@ function getBabelConfig( case UMD_DEV: case UMD_PROD: case UMD_PROFILING: + case UMD_TESTING: case NODE_DEV: case NODE_PROD: case NODE_PROFILING: + case NODE_TESTING: return Object.assign({}, options, { plugins: options.plugins.concat([ // Use object-assign polyfill in open source @@ -207,13 +213,16 @@ function getFormat(bundleType) { case UMD_DEV: case UMD_PROD: case UMD_PROFILING: + case UMD_TESTING: return `umd`; case NODE_DEV: case NODE_PROD: case NODE_PROFILING: + case NODE_TESTING: case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: case RN_OSS_DEV: case RN_OSS_PROD: case RN_OSS_PROFILING: @@ -234,12 +243,16 @@ function getFilename(name, globalName, bundleType) { return `${name}.production.min.js`; case UMD_PROFILING: return `${name}.profiling.min.js`; + case UMD_TESTING: + return `${name}.testing.js`; case NODE_DEV: return `${name}.development.js`; case NODE_PROD: return `${name}.production.min.js`; case NODE_PROFILING: return `${name}.profiling.min.js`; + case NODE_TESTING: + return `${name}.testing.js`; case FB_WWW_DEV: case RN_OSS_DEV: case RN_FB_DEV: @@ -252,14 +265,19 @@ function getFilename(name, globalName, bundleType) { case RN_FB_PROFILING: case RN_OSS_PROFILING: return `${globalName}-profiling.js`; + case FB_WWW_TESTING: + return `${globalName}-testing.js`; } } function isProductionBundleType(bundleType) { switch (bundleType) { case UMD_DEV: + case UMD_TESTING: case NODE_DEV: + case NODE_TESTING: case FB_WWW_DEV: + case FB_WWW_TESTING: case RN_OSS_DEV: case RN_FB_DEV: return false; @@ -283,14 +301,17 @@ function isProfilingBundleType(bundleType) { switch (bundleType) { case FB_WWW_DEV: case FB_WWW_PROD: + case FB_WWW_TESTING: case NODE_DEV: case NODE_PROD: + case NODE_TESTING: case RN_FB_DEV: case RN_FB_PROD: case RN_OSS_DEV: case RN_OSS_PROD: case UMD_DEV: case UMD_PROD: + case UMD_TESTING: return false; case FB_WWW_PROFILING: case NODE_PROFILING: @@ -303,6 +324,33 @@ function isProfilingBundleType(bundleType) { } } +function isTestingBuildBundleType(bundleType) { + switch (bundleType) { + case FB_WWW_DEV: + case FB_WWW_PROD: + case FB_WWW_PROFILING: + case NODE_DEV: + case NODE_PROD: + case NODE_PROFILING: + case RN_FB_DEV: + case RN_FB_PROD: + case RN_FB_PROFILING: + case RN_OSS_DEV: + case RN_OSS_PROD: + case RN_OSS_PROFILING: + case UMD_DEV: + case UMD_PROD: + case UMD_PROFILING: + return false; + case FB_WWW_TESTING: + case NODE_TESTING: + case UMD_TESTING: + return true; + default: + throw new Error(`Unknown type: ${bundleType}`); + } +} + function forbidFBJSImports() { return { name: 'forbidFBJSImports', @@ -332,14 +380,17 @@ function getPlugins( const forks = Modules.getForks(bundleType, entry, moduleType); const isProduction = isProductionBundleType(bundleType); const isProfiling = isProfilingBundleType(bundleType); + const isTestingBuild = isTestingBuildBundleType(bundleType); const isUMDBundle = bundleType === UMD_DEV || bundleType === UMD_PROD || - bundleType === UMD_PROFILING; + bundleType === UMD_PROFILING || + bundleType === UMD_TESTING; const isFBBundle = bundleType === FB_WWW_DEV || bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING; + bundleType === FB_WWW_PROFILING || + bundleType === FB_WWW_TESTING; const isRNBundle = bundleType === RN_OSS_DEV || bundleType === RN_OSS_PROD || @@ -391,6 +442,7 @@ function getPlugins( __DEV__: isProduction ? 'false' : 'true', __PROFILE__: isProfiling || !isProduction ? 'true' : 'false', __UMD__: isUMDBundle ? 'true' : 'false', + __TEST__: isTestingBuild ? 'true' : 'false', 'process.env.NODE_ENV': isProduction ? "'production'" : "'development'", __EXPERIMENTAL__, }), @@ -488,7 +540,8 @@ async function createBundle(bundle, bundleType) { const isFBBundle = bundleType === FB_WWW_DEV || bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING; + bundleType === FB_WWW_PROFILING || + bundleType === FB_WWW_TESTING; if (isFBBundle) { const resolvedFBEntry = resolvedEntry.replace('.js', '.fb.js'); if (fs.existsSync(resolvedFBEntry)) { @@ -499,7 +552,8 @@ async function createBundle(bundle, bundleType) { const shouldBundleDependencies = bundleType === UMD_DEV || bundleType === UMD_PROD || - bundleType === UMD_PROFILING; + bundleType === UMD_PROFILING || + bundleType === UMD_TESTING; const peerGlobals = Modules.getPeerGlobals(bundle.externals, bundleType); let externals = Object.keys(peerGlobals); if (!shouldBundleDependencies) { @@ -545,7 +599,8 @@ async function createBundle(bundle, bundleType) { legacy: bundleType === FB_WWW_DEV || bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING, + bundleType === FB_WWW_PROFILING || + bundleType === FB_WWW_TESTING, }; const [mainOutputPath, ...otherOutputPaths] = Packaging.getBundleOutputPaths( bundleType, @@ -681,9 +736,11 @@ async function buildEverything() { [bundle, UMD_DEV], [bundle, UMD_PROD], [bundle, UMD_PROFILING], + [bundle, UMD_TESTING], [bundle, NODE_DEV], [bundle, NODE_PROD], [bundle, NODE_PROFILING], + [bundle, NODE_TESTING], [bundle, RN_OSS_DEV], [bundle, RN_OSS_PROD], [bundle, RN_OSS_PROFILING] @@ -695,6 +752,7 @@ async function buildEverything() { [bundle, FB_WWW_DEV], [bundle, FB_WWW_PROD], [bundle, FB_WWW_PROFILING], + [bundle, FB_WWW_TESTING], [bundle, RN_FB_DEV], [bundle, RN_FB_PROD], [bundle, RN_FB_PROFILING] diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index aefbb65e4f1b1..b4280a315ff01 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -4,12 +4,15 @@ const bundleTypes = { UMD_DEV: 'UMD_DEV', UMD_PROD: 'UMD_PROD', UMD_PROFILING: 'UMD_PROFILING', + UMD_TESTING: 'UMD_TESTING', NODE_DEV: 'NODE_DEV', NODE_PROD: 'NODE_PROD', NODE_PROFILING: 'NODE_PROFILING', + NODE_TESTING: 'NODE_TESTING', FB_WWW_DEV: 'FB_WWW_DEV', FB_WWW_PROD: 'FB_WWW_PROD', FB_WWW_PROFILING: 'FB_WWW_PROFILING', + FB_WWW_TESTING: 'FB_WWW_TESTING', RN_OSS_DEV: 'RN_OSS_DEV', RN_OSS_PROD: 'RN_OSS_PROD', RN_OSS_PROFILING: 'RN_OSS_PROFILING', @@ -22,12 +25,15 @@ const { UMD_DEV, UMD_PROD, UMD_PROFILING, + UMD_TESTING, NODE_DEV, NODE_PROD, NODE_PROFILING, + NODE_TESTING, FB_WWW_DEV, FB_WWW_PROD, FB_WWW_PROFILING, + FB_WWW_TESTING, RN_OSS_DEV, RN_OSS_PROD, RN_OSS_PROFILING, @@ -64,11 +70,14 @@ const bundles = [ UMD_DEV, UMD_PROD, UMD_PROFILING, + UMD_TESTING, NODE_DEV, NODE_PROD, + NODE_TESTING, FB_WWW_DEV, FB_WWW_PROD, FB_WWW_PROFILING, + FB_WWW_TESTING, ], moduleType: ISOMORPHIC, entry: 'react', @@ -82,12 +91,15 @@ const bundles = [ UMD_DEV, UMD_PROD, UMD_PROFILING, + UMD_TESTING, NODE_DEV, NODE_PROD, NODE_PROFILING, + NODE_TESTING, FB_WWW_DEV, FB_WWW_PROD, FB_WWW_PROFILING, + FB_WWW_TESTING, ], moduleType: RENDERER, entry: 'react-dom', @@ -517,7 +529,7 @@ const bundles = [ externals: [], }, - /******* ESLint Plugin for Hooks (proposal) *******/ + /******* ESLint Plugin for Hooks *******/ { // TODO: it's awkward to create a bundle for this but if we don't, the package // won't get copied. We also can't create just DEV bundle because it contains a diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index eeb0c1caee8a7..2167fb63b3329 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -1,23 +1,25 @@ 'use strict'; -const bundleTypes = require('./bundles').bundleTypes; -const moduleTypes = require('./bundles').moduleTypes; +const {bundleTypes, moduleTypes} = require('./bundles'); const inlinedHostConfigs = require('../shared/inlinedHostConfigs'); -const UMD_DEV = bundleTypes.UMD_DEV; -const UMD_PROD = bundleTypes.UMD_PROD; -const UMD_PROFILING = bundleTypes.UMD_PROFILING; -const FB_WWW_DEV = bundleTypes.FB_WWW_DEV; -const FB_WWW_PROD = bundleTypes.FB_WWW_PROD; -const FB_WWW_PROFILING = bundleTypes.FB_WWW_PROFILING; -const RN_OSS_DEV = bundleTypes.RN_OSS_DEV; -const RN_OSS_PROD = bundleTypes.RN_OSS_PROD; -const RN_OSS_PROFILING = bundleTypes.RN_OSS_PROFILING; -const RN_FB_DEV = bundleTypes.RN_FB_DEV; -const RN_FB_PROD = bundleTypes.RN_FB_PROD; -const RN_FB_PROFILING = bundleTypes.RN_FB_PROFILING; -const RENDERER = moduleTypes.RENDERER; -const RECONCILER = moduleTypes.RECONCILER; +const { + UMD_DEV, + UMD_PROD, + UMD_PROFILING, + UMD_TESTING, + FB_WWW_DEV, + FB_WWW_PROD, + FB_WWW_PROFILING, + FB_WWW_TESTING, + RN_OSS_DEV, + RN_OSS_PROD, + RN_OSS_PROFILING, + RN_FB_DEV, + RN_FB_PROD, + RN_FB_PROFILING, +} = bundleTypes; +const {RENDERER, RECONCILER} = moduleTypes; // If you need to replace a file with another file for a specific environment, // add it to this list with the logic for choosing the right replacement. @@ -28,7 +30,8 @@ const forks = Object.freeze({ if ( bundleType !== UMD_DEV && bundleType !== UMD_PROD && - bundleType !== UMD_PROFILING + bundleType !== UMD_PROFILING && + bundleType !== UMD_TESTING ) { // It's only relevant for UMD bundles since that's where the duplication // happens. Other bundles just require('object-assign') anyway. @@ -103,6 +106,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return 'shared/forks/ReactFeatureFlags.test-renderer.www.js'; } return 'shared/forks/ReactFeatureFlags.test-renderer.js'; @@ -111,6 +115,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return 'shared/forks/ReactFeatureFlags.www.js'; } } @@ -122,6 +127,7 @@ const forks = Object.freeze({ case UMD_DEV: case UMD_PROD: case UMD_PROFILING: + case UMD_TESTING: if (dependencies.indexOf('react') === -1) { // It's only safe to use this fork for modules that depend on React, // because they read the re-exported API from the SECRET_INTERNALS object. @@ -141,6 +147,7 @@ const forks = Object.freeze({ case UMD_DEV: case UMD_PROD: case UMD_PROFILING: + case UMD_TESTING: if (dependencies.indexOf('react') === -1) { // It's only safe to use this fork for modules that depend on React, // because they read the re-exported API from the SECRET_INTERNALS object. @@ -159,7 +166,8 @@ const forks = Object.freeze({ if ( bundleType === FB_WWW_DEV || bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING + bundleType === FB_WWW_PROFILING || + bundleType === FB_WWW_TESTING ) { return 'scheduler/src/forks/SchedulerFeatureFlags.www.js'; } @@ -181,6 +189,7 @@ const forks = Object.freeze({ 'shared/consoleWithStackDev': (bundleType, entry) => { switch (bundleType) { case FB_WWW_DEV: + case FB_WWW_TESTING: return 'shared/forks/consoleWithStackDev.www.js'; default: return null; @@ -194,6 +203,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return 'react/src/forks/ReactCurrentOwner.www.js'; default: return null; @@ -207,6 +217,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return 'react/src/forks/ReactCurrentDispatcher.www.js'; default: return null; @@ -218,6 +229,7 @@ const forks = Object.freeze({ case UMD_DEV: case UMD_PROD: case UMD_PROFILING: + case UMD_TESTING: return 'react/src/forks/ReactSharedInternals.umd.js'; default: return null; @@ -230,6 +242,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return 'shared/forks/invokeGuardedCallbackImpl.www.js'; default: return null; @@ -242,6 +255,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: // Use the www fork which shows an error dialog. return 'react-reconciler/src/forks/ReactFiberErrorDialog.www.js'; case RN_OSS_DEV: @@ -378,6 +392,7 @@ const forks = Object.freeze({ case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: // Use the www fork which is integrated with TimeSlice profiling. return 'react-dom/src/events/forks/EventListener-www.js'; default: diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js index 968f98bc3f081..e748e56b25286 100644 --- a/scripts/rollup/modules.js +++ b/scripts/rollup/modules.js @@ -1,11 +1,12 @@ 'use strict'; const forks = require('./forks'); -const bundleTypes = require('./bundles').bundleTypes; - -const UMD_DEV = bundleTypes.UMD_DEV; -const UMD_PROD = bundleTypes.UMD_PROD; -const UMD_PROFILING = bundleTypes.UMD_PROFILING; +const { + UMD_DEV, + UMD_PROD, + UMD_PROFILING, + UMD_TESTING, +} = require('./bundles').bundleTypes; // For any external that is used in a DEV-only condition, explicitly // specify whether it has side effects during import or not. This lets @@ -40,7 +41,8 @@ function getPeerGlobals(externals, bundleType) { !knownGlobals[name] && (bundleType === UMD_DEV || bundleType === UMD_PROD || - bundleType === UMD_PROFILING) + bundleType === UMD_PROFILING || + bundleType === UMD_TESTING) ) { throw new Error('Cannot build UMD without a global name for: ' + name); } diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js index c5ed04df5ab6c..98a7ad14c806d 100644 --- a/scripts/rollup/packaging.js +++ b/scripts/rollup/packaging.js @@ -13,12 +13,15 @@ const { UMD_DEV, UMD_PROD, UMD_PROFILING, + UMD_TESTING, NODE_DEV, NODE_PROD, NODE_PROFILING, + NODE_TESTING, FB_WWW_DEV, FB_WWW_PROD, FB_WWW_PROFILING, + FB_WWW_TESTING, RN_OSS_DEV, RN_OSS_PROD, RN_OSS_PROFILING, @@ -39,10 +42,12 @@ function getBundleOutputPaths(bundleType, filename, packageName) { case NODE_DEV: case NODE_PROD: case NODE_PROFILING: + case NODE_TESTING: return [`build/node_modules/${packageName}/cjs/${filename}`]; case UMD_DEV: case UMD_PROD: case UMD_PROFILING: + case UMD_TESTING: return [ `build/node_modules/${packageName}/umd/${filename}`, `build/dist/${filename}`, @@ -50,6 +55,7 @@ function getBundleOutputPaths(bundleType, filename, packageName) { case FB_WWW_DEV: case FB_WWW_PROD: case FB_WWW_PROFILING: + case FB_WWW_TESTING: return [`build/facebook-www/${filename}`]; case RN_OSS_DEV: case RN_OSS_PROD: diff --git a/scripts/rollup/wrappers.js b/scripts/rollup/wrappers.js index 7f17491936968..5db32f2fbd0bb 100644 --- a/scripts/rollup/wrappers.js +++ b/scripts/rollup/wrappers.js @@ -1,25 +1,30 @@ 'use strict'; -const Bundles = require('./bundles'); +const {bundleTypes, moduleTypes} = require('./bundles'); const reactVersion = require('../../package.json').version; -const UMD_DEV = Bundles.bundleTypes.UMD_DEV; -const UMD_PROD = Bundles.bundleTypes.UMD_PROD; -const UMD_PROFILING = Bundles.bundleTypes.UMD_PROFILING; -const NODE_DEV = Bundles.bundleTypes.NODE_DEV; -const NODE_PROD = Bundles.bundleTypes.NODE_PROD; -const NODE_PROFILING = Bundles.bundleTypes.NODE_PROFILING; -const FB_WWW_DEV = Bundles.bundleTypes.FB_WWW_DEV; -const FB_WWW_PROD = Bundles.bundleTypes.FB_WWW_PROD; -const FB_WWW_PROFILING = Bundles.bundleTypes.FB_WWW_PROFILING; -const RN_OSS_DEV = Bundles.bundleTypes.RN_OSS_DEV; -const RN_OSS_PROD = Bundles.bundleTypes.RN_OSS_PROD; -const RN_OSS_PROFILING = Bundles.bundleTypes.RN_OSS_PROFILING; -const RN_FB_DEV = Bundles.bundleTypes.RN_FB_DEV; -const RN_FB_PROD = Bundles.bundleTypes.RN_FB_PROD; -const RN_FB_PROFILING = Bundles.bundleTypes.RN_FB_PROFILING; - -const RECONCILER = Bundles.moduleTypes.RECONCILER; +const { + UMD_DEV, + UMD_PROD, + UMD_PROFILING, + UMD_TESTING, + NODE_DEV, + NODE_PROD, + NODE_PROFILING, + NODE_TESTING, + FB_WWW_DEV, + FB_WWW_PROD, + FB_WWW_PROFILING, + FB_WWW_TESTING, + RN_OSS_DEV, + RN_OSS_PROD, + RN_OSS_PROFILING, + RN_FB_DEV, + RN_FB_PROD, + RN_FB_PROFILING, +} = bundleTypes; + +const {RECONCILER} = moduleTypes; const license = ` * Copyright (c) Facebook, Inc. and its affiliates. * @@ -60,6 +65,16 @@ ${license} ${source}`; }, + /***************** UMD_TESTING *****************/ + [UMD_TESTING](source, globalName, filename, moduleType) { + return `/** @license React v${reactVersion} + * ${filename} + * +${license} + */ +${source}`; + }, + /***************** NODE_DEV *****************/ [NODE_DEV](source, globalName, filename, moduleType) { return `/** @license React v${reactVersion} @@ -125,6 +140,25 @@ ${ ${source}`; }, + /***************** NODE_TESTING *****************/ + [NODE_TESTING](source, globalName, filename, moduleType) { + return `/** @license React v${reactVersion} + * ${filename} + * +${license} + */ +${ + globalName === 'ReactNoopRenderer' || + globalName === 'ReactNoopRendererPersistent' + ? // React Noop needs regenerator runtime because it uses + // generators but GCC doesn't handle them in the output. + // So we use Babel for them. + `const regeneratorRuntime = require("regenerator-runtime");` + : `` +} +${source}`; + }, + /****************** FB_WWW_DEV ******************/ [FB_WWW_DEV](source, globalName, filename, moduleType) { return `/** @@ -167,6 +201,19 @@ ${license} * @preserve-invariant-messages */ +${source}`; + }, + + /****************** FB_WWW_TESTING ******************/ + [FB_WWW_TESTING](source, globalName, filename, moduleType) { + return `/** +${license} + * + * @noflow + * @preventMunge + * @preserve-invariant-messages + */ + ${source}`; }, @@ -293,6 +340,21 @@ ${source} ${license} */ module.exports = function $$$reconciler($$$hostConfig) { +${source} + var $$$renderer = module.exports; + module.exports = $$$reconciler; + return $$$renderer; +};`; + }, + + /***************** NODE_TESTING (reconciler only) *****************/ + [NODE_TESTING](source, globalName, filename, moduleType) { + return `/** @license React v${reactVersion} + * ${filename} + * +${license} + */ +module.exports = function $$$reconciler($$$hostConfig) { ${source} var $$$renderer = module.exports; module.exports = $$$reconciler;