diff --git a/packages/react-dom-bindings/src/client/ReactDOMContainer.js b/packages/react-dom-bindings/src/client/ReactDOMContainer.js new file mode 100644 index 0000000000000..0af0f13a790a8 --- /dev/null +++ b/packages/react-dom-bindings/src/client/ReactDOMContainer.js @@ -0,0 +1,42 @@ +/** + * Copyright (c) Meta Platforms, Inc. and 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 {disableCommentsAsDOMContainers} from 'shared/ReactFeatureFlags'; + +import { + ELEMENT_NODE, + COMMENT_NODE, + DOCUMENT_NODE, + DOCUMENT_FRAGMENT_NODE, +} from './HTMLNodeType'; + +export function isValidContainer(node: any): boolean { + return !!( + node && + (node.nodeType === ELEMENT_NODE || + node.nodeType === DOCUMENT_NODE || + node.nodeType === DOCUMENT_FRAGMENT_NODE || + (!disableCommentsAsDOMContainers && + node.nodeType === COMMENT_NODE && + (node: any).nodeValue === ' react-mount-point-unstable ')) + ); +} + +// TODO: Remove this function which also includes comment nodes. +// We only use it in places that are currently more relaxed. +export function isValidContainerLegacy(node: any): boolean { + return !!( + node && + (node.nodeType === ELEMENT_NODE || + node.nodeType === DOCUMENT_NODE || + node.nodeType === DOCUMENT_FRAGMENT_NODE || + (node.nodeType === COMMENT_NODE && + (node: any).nodeValue === ' react-mount-point-unstable ')) + ); +} diff --git a/packages/react-dom/client.js b/packages/react-dom/client.js index 5011372e2faec..4d57f56da05f9 100644 --- a/packages/react-dom/client.js +++ b/packages/react-dom/client.js @@ -7,50 +7,4 @@ * @flow */ -'use strict'; - -import type {ReactNodeList} from 'shared/ReactTypes'; -import type { - RootType, - HydrateRootOptions, - CreateRootOptions, -} from './src/client/ReactDOMRoot'; - -import { - createRoot as createRootImpl, - hydrateRoot as hydrateRootImpl, - __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE as Internals, -} from './'; - -export function createRoot( - container: Element | Document | DocumentFragment, - options?: CreateRootOptions, -): RootType { - if (__DEV__) { - Internals.usingClientEntryPoint = true; - } - try { - return createRootImpl(container, options); - } finally { - if (__DEV__) { - Internals.usingClientEntryPoint = false; - } - } -} - -export function hydrateRoot( - container: Document | Element, - children: ReactNodeList, - options?: HydrateRootOptions, -): RootType { - if (__DEV__) { - Internals.usingClientEntryPoint = true; - } - try { - return hydrateRootImpl(container, children, options); - } finally { - if (__DEV__) { - Internals.usingClientEntryPoint = false; - } - } -} +export {createRoot, hydrateRoot, version} from './src/client/ReactDOMClient'; diff --git a/packages/react-dom/index.experimental.js b/packages/react-dom/index.experimental.js deleted file mode 100644 index 0cc5ef13e3698..0000000000000 --- a/packages/react-dom/index.experimental.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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 {default as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './src/ReactDOMSharedInternals'; -export { - createPortal, - createRoot, - hydrateRoot, - flushSync, - unstable_batchedUpdates, - unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. - useFormStatus, - useFormState, - requestFormReset, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, -} from './src/client/ReactDOM'; diff --git a/packages/react-dom/index.js b/packages/react-dom/index.js index b6f85ce628c57..ca91fdcea1cbe 100644 --- a/packages/react-dom/index.js +++ b/packages/react-dom/index.js @@ -7,25 +7,19 @@ * @flow */ -// Export all exports so that they're available in tests. -// We can't use export * from in Flow for some reason. export {default as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './src/ReactDOMSharedInternals'; export { createPortal, - createRoot, - hydrateRoot, flushSync, - unstable_batchedUpdates, - unstable_createEventHandle, - unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. - useFormStatus, - useFormState, - requestFormReset, prefetchDNS, preconnect, preload, preloadModule, preinit, preinitModule, + requestFormReset, + unstable_batchedUpdates, + useFormState, + useFormStatus, version, -} from './src/client/ReactDOM'; +} from './src/shared/ReactDOM'; diff --git a/packages/react-dom/index.stable.js b/packages/react-dom/index.stable.js deleted file mode 100644 index f5ed138d67ab6..0000000000000 --- a/packages/react-dom/index.stable.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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 {default as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './src/ReactDOMSharedInternals'; -export { - createPortal, - createRoot, - hydrateRoot, - flushSync, - unstable_batchedUpdates, - useFormStatus, - useFormState, - requestFormReset, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, -} from './src/client/ReactDOM'; diff --git a/packages/react-dom/npm/client.js b/packages/react-dom/npm/client.js index af04d9eb2ad9f..11d43b8bd285c 100644 --- a/packages/react-dom/npm/client.js +++ b/packages/react-dom/npm/client.js @@ -1,25 +1,38 @@ 'use strict'; -var m = require('react-dom'); +function checkDCE() { + /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ + if ( + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined' || + typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE !== 'function' + ) { + return; + } + if (process.env.NODE_ENV !== 'production') { + // This branch is unreachable because this function is only called + // in production, but the condition is true only in development. + // Therefore if the branch is still here, dead code elimination wasn't + // properly applied. + // Don't change the message. React DevTools relies on it. Also make sure + // this message doesn't occur elsewhere in this function, or it will cause + // a false positive. + throw new Error('^_^'); + } + try { + // Verify that the code above has been dead code eliminated (DCE'd). + __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(checkDCE); + } catch (err) { + // DevTools shouldn't crash React, no matter what. + // We should still report in case we break this code. + console.error(err); + } +} + if (process.env.NODE_ENV === 'production') { - exports.createRoot = m.createRoot; - exports.hydrateRoot = m.hydrateRoot; + // DCE check should happen before ReactDOM bundle executes so that + // DevTools can report bad minification during injection. + checkDCE(); + module.exports = require('./cjs/react-dom-client.production.min.js'); } else { - var i = m.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE; - exports.createRoot = function (c, o) { - i.usingClientEntryPoint = true; - try { - return m.createRoot(c, o); - } finally { - i.usingClientEntryPoint = false; - } - }; - exports.hydrateRoot = function (c, h, o) { - i.usingClientEntryPoint = true; - try { - return m.hydrateRoot(c, h, o); - } finally { - i.usingClientEntryPoint = false; - } - }; + module.exports = require('./cjs/react-dom-client.development.js'); } diff --git a/packages/react-dom/npm/profiling.js b/packages/react-dom/npm/profiling.js index 91f89f07ffe13..ad37f6ec7de04 100644 --- a/packages/react-dom/npm/profiling.js +++ b/packages/react-dom/npm/profiling.js @@ -32,7 +32,7 @@ if (process.env.NODE_ENV === 'production') { // DCE check should happen before ReactDOM bundle executes so that // DevTools can report bad minification during injection. checkDCE(); - module.exports = require('./cjs/react-dom.profiling.min.js'); + module.exports = require('./cjs/react-dom-profiling.profiling.min.js'); } else { - module.exports = require('./cjs/react-dom.development.js'); + module.exports = require('./cjs/react-dom-profiling.development.js'); } diff --git a/packages/react-dom/npm/server-rendering-stub.js b/packages/react-dom/npm/server-rendering-stub.js deleted file mode 100644 index fa2464a3e59fe..0000000000000 --- a/packages/react-dom/npm/server-rendering-stub.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -if (process.env.NODE_ENV === 'production') { - module.exports = require('./cjs/react-dom-server-rendering-stub.production.min.js'); -} else { - module.exports = require('./cjs/react-dom-server-rendering-stub.development.js'); -} diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index 3c4b8bd50a2bd..170471ad8ac1a 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -31,7 +31,6 @@ "profiling.js", "profiling.react-server.js", "react-dom.react-server.js", - "server-rendering-stub.js", "server.browser.js", "server.bun.js", "server.edge.js", @@ -107,7 +106,6 @@ "react-server": "./static.react-server.js", "default": "./static.node.js" }, - "./server-rendering-stub": "./server-rendering-stub.js", "./profiling": { "react-server": "./profiling.react-server.js", "default": "./profiling.js" diff --git a/packages/react-dom/profiling.js b/packages/react-dom/profiling.js new file mode 100644 index 0000000000000..cdd2502fc007a --- /dev/null +++ b/packages/react-dom/profiling.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + */ + +// This entrypoint should track the /client entrypoint +export * from './client'; +export * from './index'; diff --git a/packages/react-dom/server-rendering-stub.js b/packages/react-dom/server-rendering-stub.js deleted file mode 100644 index 30f4f80e38903..0000000000000 --- a/packages/react-dom/server-rendering-stub.js +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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 all exports so that they're available in tests. -// We can't use export * from in Flow for some reason. - -import ReactVersion from 'shared/ReactVersion'; -export {ReactVersion as version}; - -export {default as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './src/ReactDOMSharedInternals'; - -export { - createPortal, - flushSync, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - useFormStatus, - useFormState, - unstable_batchedUpdates, -} from './src/server/ReactDOMServerRenderingStub'; - -import type {FormStatus} from 'react-dom-bindings/src/shared/ReactDOMFormActions'; -import { - useFormStatus, - useFormState, -} from './src/server/ReactDOMServerRenderingStub'; -import type {Awaited} from 'shared/ReactTypes'; - -export function experimental_useFormStatus(): FormStatus { - if (__DEV__) { - console.error( - 'useFormStatus is now in canary. Remove the experimental_ prefix. ' + - 'The prefixed alias will be removed in an upcoming release.', - ); - } - return useFormStatus(); -} - -export function experimental_useFormState( - action: (Awaited, P) => S, - initialState: Awaited, - permalink?: string, -): [Awaited, (P) => void, boolean] { - if (__DEV__) { - console.error( - 'useFormState is now in canary. Remove the experimental_ prefix. ' + - 'The prefixed alias will be removed in an upcoming release.', - ); - } - return useFormState(action, initialState, permalink); -} diff --git a/packages/react-dom/index.classic.fb.js b/packages/react-dom/src/ReactDOMFB.js similarity index 88% rename from packages/react-dom/index.classic.fb.js rename to packages/react-dom/src/ReactDOMFB.js index 63c20f7e51bfe..409aa376d5c1c 100644 --- a/packages/react-dom/index.classic.fb.js +++ b/packages/react-dom/src/ReactDOMFB.js @@ -9,7 +9,7 @@ import {isEnabled} from 'react-dom-bindings/src/events/ReactDOMEventListener'; -import Internals from './src/ReactDOMSharedInternalsFB'; +import Internals from './ReactDOMSharedInternalsFB'; // For classic WWW builds, include a few internals that are already in use. Object.assign((Internals: any), { @@ -18,6 +18,8 @@ Object.assign((Internals: any), { }, }); +export {Internals as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE}; + export { createPortal, flushSync, @@ -33,7 +35,7 @@ export { preinit, preinitModule, version, -} from './src/client/ReactDOMFB'; +} from './client/ReactDOMClientFB'; export { createRoot, @@ -43,6 +45,4 @@ export { findDOMNode, unstable_renderSubtreeIntoContainer, unmountComponentAtNode, -} from './src/client/ReactDOMRootFB'; - -export {Internals as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE}; +} from './client/ReactDOMRootFB'; diff --git a/packages/react-dom/index.modern.fb.js b/packages/react-dom/src/ReactDOMFB.modern.js similarity index 79% rename from packages/react-dom/index.modern.fb.js rename to packages/react-dom/src/ReactDOMFB.modern.js index 861febfa5e37b..0dd47906019ff 100644 --- a/packages/react-dom/index.modern.fb.js +++ b/packages/react-dom/src/ReactDOMFB.modern.js @@ -7,7 +7,8 @@ * @flow */ -export {default as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './src/ReactDOMSharedInternalsFB'; +export {default as __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE} from './ReactDOMSharedInternalsFB'; + export { createPortal, flushSync, @@ -24,6 +25,6 @@ export { preinit, preinitModule, version, -} from './src/client/ReactDOMFB'; +} from './client/ReactDOMClientFB'; -export {createRoot, hydrateRoot} from './src/client/ReactDOMRootFB'; +export {createRoot, hydrateRoot} from './client/ReactDOMRootFB'; diff --git a/packages/react-dom/src/ReactDOMServer.js b/packages/react-dom/src/ReactDOMReactServer.js similarity index 100% rename from packages/react-dom/src/ReactDOMServer.js rename to packages/react-dom/src/ReactDOMReactServer.js diff --git a/packages/react-dom/src/ReactDOMSharedInternals.js b/packages/react-dom/src/ReactDOMSharedInternals.js index 0a0f97866738a..0d11b81718917 100644 --- a/packages/react-dom/src/ReactDOMSharedInternals.js +++ b/packages/react-dom/src/ReactDOMSharedInternals.js @@ -20,11 +20,6 @@ type ReactDOMInternals = { | (( componentOrElement: React$Component, ) => null | Element | Text), - usingClientEntryPoint: boolean, -}; - -export type ReactDOMInternalsDev = ReactDOMInternals & { - usingClientEntryPoint: boolean, }; function noop() {} @@ -52,7 +47,6 @@ const Internals: ReactDOMInternals = { d /* ReactDOMCurrentDispatcher */: DefaultDispatcher, p /* currentUpdatePriority */: NoEventPriority, findDOMNode: null, - usingClientEntryPoint: false, }; export default Internals; diff --git a/packages/react-dom/unstable_testing.modern.fb.js b/packages/react-dom/src/ReactDOMTestingFB.js similarity index 57% rename from packages/react-dom/unstable_testing.modern.fb.js rename to packages/react-dom/src/ReactDOMTestingFB.js index 244d82025105f..1d7a4c8fb13bc 100644 --- a/packages/react-dom/unstable_testing.modern.fb.js +++ b/packages/react-dom/src/ReactDOMTestingFB.js @@ -7,23 +7,8 @@ * @flow */ -export { - createPortal, - flushSync, - unstable_batchedUpdates, - unstable_createEventHandle, - useFormStatus, - useFormState, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, - __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, -} from './index.modern.fb.js'; -export {createRoot, hydrateRoot} from './client.js'; +export * from './ReactDOMFB'; + export { createComponentSelector, createHasPseudoClassSelector, diff --git a/packages/react-dom/src/ReactDOMTestingFB.modern.js b/packages/react-dom/src/ReactDOMTestingFB.modern.js new file mode 100644 index 0000000000000..7d60bf6d4e0b8 --- /dev/null +++ b/packages/react-dom/src/ReactDOMTestingFB.modern.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) Meta Platforms, Inc. and 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 './ReactDOMFB.modern'; + +export { + createComponentSelector, + createHasPseudoClassSelector, + createRoleSelector, + createTestNameSelector, + createTextSelector, + getFindAllNodesFailureDescription, + findAllNodes, + findBoundingRects, + focusWithin, + observeVisibleRects, +} from 'react-reconciler/src/ReactFiberReconciler'; diff --git a/packages/react-dom/src/__tests__/ReactDOM-test.js b/packages/react-dom/src/__tests__/ReactDOM-test.js index e4d7b0ddec658..72fa57fb1fd5e 100644 --- a/packages/react-dom/src/__tests__/ReactDOM-test.js +++ b/packages/react-dom/src/__tests__/ReactDOM-test.js @@ -22,11 +22,11 @@ describe('ReactDOM', () => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); + ReactDOMServer = require('react-dom/server'); findDOMNode = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE .findDOMNode; - ReactDOMClient = require('react-dom/client'); - ReactDOMServer = require('react-dom/server'); act = require('internal-test-utils').act; }); diff --git a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js index f36f45a1e28fd..993e6e5a641fa 100644 --- a/packages/react-dom/src/__tests__/ReactDOMRoot-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMRoot-test.js @@ -47,28 +47,6 @@ describe('ReactDOMRoot', () => { expect(container.textContent).toEqual('Hi'); }); - // @gate !www || !__DEV__ - it('warns if you import createRoot from react-dom', async () => { - expect(() => ReactDOM.createRoot(container)).toErrorDev( - 'You are importing createRoot from "react-dom" which is not supported. ' + - 'You should instead import it from "react-dom/client".', - { - withoutStack: true, - }, - ); - }); - - // @gate !www || !__DEV__ - it('warns if you import hydrateRoot from react-dom', async () => { - expect(() => ReactDOM.hydrateRoot(container, null)).toErrorDev( - 'You are importing hydrateRoot from "react-dom" which is not supported. ' + - 'You should instead import it from "react-dom/client".', - { - withoutStack: true, - }, - ); - }); - it('warns if a callback parameter is provided to render', async () => { const callback = jest.fn(); const root = ReactDOMClient.createRoot(container); diff --git a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js index cd0c360b41c37..36f7c95ba6531 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerPartialHydration-test.internal.js @@ -1479,7 +1479,7 @@ describe('ReactDOMServerPartialHydration', () => { expect(span.className).toBe('hi'); }); - // @gate experimental || www + // @gate www it('blocks updates to hydrate the content first if props changed at idle priority', async () => { let suspend = false; let resolve; diff --git a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js index 6a5aff96b3c1f..cbc4b5256c185 100644 --- a/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js +++ b/packages/react-dom/src/__tests__/ReactDOMServerSelectiveHydration-test.internal.js @@ -1333,7 +1333,7 @@ describe('ReactDOMServerSelectiveHydration', () => { await waitForAll(['App', 'C', 'B', 'A']); }); - // @gate experimental || www + // @gate www it('hydrates before an update even if hydration moves away from it', async () => { function Child({text}) { Scheduler.log(text); @@ -1677,7 +1677,7 @@ describe('ReactDOMServerSelectiveHydration', () => { expect(initialSpan).toBe(spanRef); }); - // @gate experimental || www + // @gate www it('can force hydration in response to continuous update', async () => { function Child({text}) { Scheduler.log(`Child ${text}`); @@ -1746,7 +1746,7 @@ describe('ReactDOMServerSelectiveHydration', () => { expect(initialSpan).toBe(spanRef); }); - // @gate experimental || www + // @gate www it('regression test: can unwind context on selective hydration interruption', async () => { const Context = React.createContext('DefaultContext'); diff --git a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js index 5cb46b93b5f8c..50ba64a478888 100644 --- a/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMSuspensePlaceholder-test.js @@ -26,10 +26,10 @@ describe('ReactDOMSuspensePlaceholder', () => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); findDOMNode = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE .findDOMNode; - ReactDOMClient = require('react-dom/client'); Scheduler = require('scheduler'); act = require('internal-test-utils').act; assertLog = require('internal-test-utils').assertLog; diff --git a/packages/react-dom/src/__tests__/ReactEmptyComponent-test.js b/packages/react-dom/src/__tests__/ReactEmptyComponent-test.js index de22540c5432f..c985d0e048dd1 100644 --- a/packages/react-dom/src/__tests__/ReactEmptyComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactEmptyComponent-test.js @@ -26,10 +26,10 @@ describe('ReactEmptyComponent', () => { React = require('react'); ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); findDOMNode = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE .findDOMNode; - ReactDOMClient = require('react-dom/client'); Scheduler = require('scheduler'); const InternalTestUtils = require('internal-test-utils'); act = InternalTestUtils.act; diff --git a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js index a39fbb0540881..eaf49b7527e38 100644 --- a/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactLegacyCompositeComponent-test.js @@ -21,10 +21,10 @@ describe('ReactLegacyCompositeComponent', () => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); findDOMNode = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE .findDOMNode; - ReactDOMClient = require('react-dom/client'); PropTypes = require('prop-types'); act = require('internal-test-utils').act; }); diff --git a/packages/react-dom/src/__tests__/ReactUpdates-test.js b/packages/react-dom/src/__tests__/ReactUpdates-test.js index 322907635d798..9a10a8d061b5f 100644 --- a/packages/react-dom/src/__tests__/ReactUpdates-test.js +++ b/packages/react-dom/src/__tests__/ReactUpdates-test.js @@ -24,10 +24,10 @@ describe('ReactUpdates', () => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); + ReactDOMClient = require('react-dom/client'); findDOMNode = ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE .findDOMNode; - ReactDOMClient = require('react-dom/client'); act = require('internal-test-utils').act; Scheduler = require('scheduler'); diff --git a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js deleted file mode 100644 index 9425ca1dd1f15..0000000000000 --- a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @emails react-core - */ - -'use strict'; - -let React; -let ReactDOM; -let ReactDOMFizzServer; - -describe('react-dom-server-rendering-stub', () => { - beforeEach(() => { - jest.mock('react-dom', () => require('react-dom/server-rendering-stub')); - - React = require('react'); - ReactDOM = require('react-dom'); - ReactDOMFizzServer = require('react-dom/server'); - }); - - it('exports a version', () => { - expect(ReactDOM.version).toBeTruthy(); - }); - - it('exports that are expected to be client only in the future are not exported', () => { - expect(ReactDOM.createRoot).toBe(undefined); - expect(ReactDOM.hydrateRoot).toBe(undefined); - expect(ReactDOM.findDOMNode).toBe(undefined); - expect( - ReactDOM.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE - .findDOMNode, - ).toBe(null); - expect(ReactDOM.hydrate).toBe(undefined); - expect(ReactDOM.render).toBe(undefined); - expect(ReactDOM.unmountComponentAtNode).toBe(undefined); - expect(ReactDOM.unstable_createEventHandle).toBe(undefined); - expect(ReactDOM.unstable_renderSubtreeIntoContainer).toBe(undefined); - expect(ReactDOM.unstable_runWithPriority).toBe(undefined); - }); - - it('provides preload, preloadModule, preinit, and preinitModule exports', async () => { - function App() { - ReactDOM.preload('foo', {as: 'style'}); - ReactDOM.preloadModule('foomodule'); - ReactDOM.preinit('bar', {as: 'style'}); - ReactDOM.preinitModule('barmodule'); - return
foo
; - } - const html = ReactDOMFizzServer.renderToString(); - expect(html).toEqual( - '
foo
', - ); - }); - - it('provides preconnect and prefetchDNS exports', async () => { - function App() { - ReactDOM.preconnect('foo', {crossOrigin: 'use-credentials'}); - ReactDOM.prefetchDNS('bar'); - return
foo
; - } - const html = ReactDOMFizzServer.renderToString(); - expect(html).toEqual( - '
foo
', - ); - }); - - it('provides a stub for createPortal', async () => { - expect(() => { - ReactDOM.createPortal(); - }).toThrow( - 'createPortal was called on the server. Portals are not currently supported on the server. Update your program to conditionally call createPortal on the client only.', - ); - }); - - it('provides a stub for flushSync', async () => { - let x = false; - expect(() => { - ReactDOM.flushSync(() => (x = true)); - }).toThrow( - 'flushSync was called on the server. This is likely caused by a function being called during render or in module scope that was intended to be called from an effect or event handler. Update your to not call flushSync no the server.', - ); - expect(x).toBe(false); - }); - - // @gate enableAsyncActions - it('exports useFormStatus', async () => { - function App() { - const {pending} = ReactDOM.useFormStatus(); - return 'Pending: ' + pending; - } - - const result = await ReactDOMFizzServer.renderToStaticMarkup(); - expect(result).toEqual('Pending: false'); - }); -}); diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js deleted file mode 100644 index 913b9359037dc..0000000000000 --- a/packages/react-dom/src/client/ReactDOM.js +++ /dev/null @@ -1,204 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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 {ReactNodeList} from 'shared/ReactTypes'; -import type { - RootType, - HydrateRootOptions, - CreateRootOptions, -} from './ReactDOMRoot'; - -import {disableLegacyMode} from 'shared/ReactFeatureFlags'; -import { - createRoot as createRootImpl, - hydrateRoot as hydrateRootImpl, - isValidContainer, -} from './ReactDOMRoot'; -import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle'; -import {runWithPriority} from 'react-dom-bindings/src/client/ReactDOMUpdatePriority'; -import {flushSync as flushSyncIsomorphic} from '../shared/ReactDOMFlushSync'; - -import { - flushSyncFromReconciler as flushSyncWithoutWarningIfAlreadyRendering, - isAlreadyRendering, - injectIntoDevTools, - findHostInstance, -} from 'react-reconciler/src/ReactFiberReconciler'; -import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal'; -import {canUseDOM} from 'shared/ExecutionEnvironment'; -import ReactVersion from 'shared/ReactVersion'; - -import {getClosestInstanceFromNode} from 'react-dom-bindings/src/client/ReactDOMComponentTree'; -import Internals from '../ReactDOMSharedInternals'; - -export { - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, -} from '../shared/ReactDOMFloat'; -export { - useFormStatus, - useFormState, - requestFormReset, -} from 'react-dom-bindings/src/shared/ReactDOMFormActions'; - -if (__DEV__) { - if ( - typeof Map !== 'function' || - // $FlowFixMe[prop-missing] Flow incorrectly thinks Map has no prototype - Map.prototype == null || - typeof Map.prototype.forEach !== 'function' || - typeof Set !== 'function' || - // $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype - Set.prototype == null || - typeof Set.prototype.clear !== 'function' || - typeof Set.prototype.forEach !== 'function' - ) { - console.error( - 'React depends on Map and Set built-in types. Make sure that you load a ' + - 'polyfill in older browsers. https://react.dev/link/react-polyfills', - ); - } -} - -function createPortal( - children: ReactNodeList, - container: Element | DocumentFragment, - key: ?string = null, -): React$Portal { - if (!isValidContainer(container)) { - throw new Error('Target container is not a DOM element.'); - } - - // TODO: pass ReactDOM portal implementation as third argument - // $FlowFixMe[incompatible-return] The Flow type is opaque but there's no way to actually create it. - return createPortalImpl(children, container, null, key); -} - -function createRoot( - container: Element | Document | DocumentFragment, - options?: CreateRootOptions, -): RootType { - if (__DEV__) { - if (!Internals.usingClientEntryPoint) { - console.error( - 'You are importing createRoot from "react-dom" which is not supported. ' + - 'You should instead import it from "react-dom/client".', - ); - } - } - return createRootImpl(container, options); -} - -function hydrateRoot( - container: Document | Element, - initialChildren: ReactNodeList, - options?: HydrateRootOptions, -): RootType { - if (__DEV__) { - if (!Internals.usingClientEntryPoint) { - console.error( - 'You are importing hydrateRoot from "react-dom" which is not supported. ' + - 'You should instead import it from "react-dom/client".', - ); - } - } - return hydrateRootImpl(container, initialChildren, options); -} - -// Overload the definition to the two valid signatures. -// Warning, this opts-out of checking the function body. -declare function flushSyncFromReconciler(fn: () => R): R; -// eslint-disable-next-line no-redeclare -declare function flushSyncFromReconciler(): void; -// eslint-disable-next-line no-redeclare -function flushSyncFromReconciler(fn: (() => R) | void): R | void { - if (__DEV__) { - if (isAlreadyRendering()) { - console.error( - 'flushSync was called from inside a lifecycle method. React cannot ' + - 'flush when React is already rendering. Consider moving this call to ' + - 'a scheduler task or micro task.', - ); - } - } - return flushSyncWithoutWarningIfAlreadyRendering(fn); -} - -const flushSync: typeof flushSyncIsomorphic = disableLegacyMode - ? flushSyncIsomorphic - : flushSyncFromReconciler; - -function findDOMNode( - componentOrElement: React$Component, -): null | Element | Text { - return findHostInstance(componentOrElement); -} - -// Expose findDOMNode on internals -Internals.findDOMNode = findDOMNode; - -function unstable_batchedUpdates(fn: (a: A) => R, a: A): R { - // batchedUpdates was a legacy mode feature that is a no-op outside of - // legacy mode. In 19, we made it an actual no-op, but we're keeping it - // for now since there may be libraries that still include it. - return fn(a); -} - -export { - createPortal, - unstable_batchedUpdates, - flushSync, - ReactVersion as version, - // exposeConcurrentModeAPIs - createRoot, - hydrateRoot, - // enableCreateEventHandleAPI - createEventHandle as unstable_createEventHandle, - // TODO: Remove this once callers migrate to alternatives. - // This should only be used by React internals. - runWithPriority as unstable_runWithPriority, -}; - -const foundDevTools = injectIntoDevTools({ - findFiberByHostInstance: getClosestInstanceFromNode, - bundleType: __DEV__ ? 1 : 0, - version: ReactVersion, - rendererPackageName: 'react-dom', -}); - -if (__DEV__) { - if (!foundDevTools && canUseDOM && window.top === window.self) { - // If we're in Chrome or Firefox, provide a download link if not installed. - if ( - (navigator.userAgent.indexOf('Chrome') > -1 && - navigator.userAgent.indexOf('Edge') === -1) || - navigator.userAgent.indexOf('Firefox') > -1 - ) { - const protocol = window.location.protocol; - // Don't warn in exotic cases like chrome-extension://. - if (/^(https?|file):$/.test(protocol)) { - // eslint-disable-next-line react-internal/no-production-logging - console.info( - '%cDownload the React DevTools ' + - 'for a better development experience: ' + - 'https://react.dev/link/react-devtools' + - (protocol === 'file:' - ? '\nYou might need to use a local HTTP server (instead of file://): ' + - 'https://react.dev/link/react-devtools-faq' - : ''), - 'font-weight:bold', - ); - } - } - } -} diff --git a/packages/react-dom/src/client/ReactDOMClient.js b/packages/react-dom/src/client/ReactDOMClient.js new file mode 100644 index 0000000000000..036fd846a47ba --- /dev/null +++ b/packages/react-dom/src/client/ReactDOMClient.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) Meta Platforms, Inc. and 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 {createRoot, hydrateRoot} from './ReactDOMRoot'; + +import { + injectIntoDevTools, + findHostInstance, +} from 'react-reconciler/src/ReactFiberReconciler'; +import {canUseDOM} from 'shared/ExecutionEnvironment'; +import ReactVersion from 'shared/ReactVersion'; + +import {getClosestInstanceFromNode} from 'react-dom-bindings/src/client/ReactDOMComponentTree'; +import Internals from 'shared/ReactDOMSharedInternals'; + +if (__DEV__) { + if ( + typeof Map !== 'function' || + // $FlowFixMe[prop-missing] Flow incorrectly thinks Map has no prototype + Map.prototype == null || + typeof Map.prototype.forEach !== 'function' || + typeof Set !== 'function' || + // $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype + Set.prototype == null || + typeof Set.prototype.clear !== 'function' || + typeof Set.prototype.forEach !== 'function' + ) { + console.error( + 'React depends on Map and Set built-in types. Make sure that you load a ' + + 'polyfill in older browsers. https://react.dev/link/react-polyfills', + ); + } +} + +function findDOMNode( + componentOrElement: React$Component, +): null | Element | Text { + return findHostInstance(componentOrElement); +} + +// Expose findDOMNode on internals +Internals.findDOMNode = findDOMNode; + +export {ReactVersion as version, createRoot, hydrateRoot}; + +const foundDevTools = injectIntoDevTools({ + findFiberByHostInstance: getClosestInstanceFromNode, + bundleType: __DEV__ ? 1 : 0, + version: ReactVersion, + rendererPackageName: 'react-dom', +}); + +if (__DEV__) { + if (!foundDevTools && canUseDOM && window.top === window.self) { + // If we're in Chrome or Firefox, provide a download link if not installed. + if ( + (navigator.userAgent.indexOf('Chrome') > -1 && + navigator.userAgent.indexOf('Edge') === -1) || + navigator.userAgent.indexOf('Firefox') > -1 + ) { + const protocol = window.location.protocol; + // Don't warn in exotic cases like chrome-extension://. + if (/^(https?|file):$/.test(protocol)) { + // eslint-disable-next-line react-internal/no-production-logging + console.info( + '%cDownload the React DevTools ' + + 'for a better development experience: ' + + 'https://react.dev/link/react-devtools' + + (protocol === 'file:' + ? '\nYou might need to use a local HTTP server (instead of file://): ' + + 'https://react.dev/link/react-devtools-faq' + : ''), + 'font-weight:bold', + ); + } + } + } +} diff --git a/packages/react-dom/src/client/ReactDOMFB.js b/packages/react-dom/src/client/ReactDOMClientFB.js similarity index 98% rename from packages/react-dom/src/client/ReactDOMFB.js rename to packages/react-dom/src/client/ReactDOMClientFB.js index 9b016b04d8dfd..1f918e3cf68c0 100644 --- a/packages/react-dom/src/client/ReactDOMFB.js +++ b/packages/react-dom/src/client/ReactDOMClientFB.js @@ -10,7 +10,7 @@ import type {ReactNodeList} from 'shared/ReactTypes'; import {disableLegacyMode} from 'shared/ReactFeatureFlags'; -import {isValidContainer} from './ReactDOMRoot'; +import {isValidContainer} from 'react-dom-bindings/src/client/ReactDOMContainer'; import {createEventHandle} from 'react-dom-bindings/src/client/ReactDOMEventHandle'; import {runWithPriority} from 'react-dom-bindings/src/client/ReactDOMUpdatePriority'; import {flushSync as flushSyncIsomorphic} from '../shared/ReactDOMFlushSync'; diff --git a/packages/react-dom/src/client/ReactDOMRoot.js b/packages/react-dom/src/client/ReactDOMRoot.js index ef573f5f790cd..aef8775de5a16 100644 --- a/packages/react-dom/src/client/ReactDOMRoot.js +++ b/packages/react-dom/src/client/ReactDOMRoot.js @@ -13,11 +13,11 @@ import type { TransitionTracingCallbacks, } from 'react-reconciler/src/ReactInternalTypes'; +import {isValidContainer} from 'react-dom-bindings/src/client/ReactDOMContainer'; import {queueExplicitHydrationTarget} from 'react-dom-bindings/src/events/ReactDOMEventReplaying'; import {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; import { allowConcurrentByDefault, - disableCommentsAsDOMContainers, enableAsyncActions, } from 'shared/ReactFeatureFlags'; @@ -82,12 +82,7 @@ import { unmarkContainerAsRoot, } from 'react-dom-bindings/src/client/ReactDOMComponentTree'; import {listenToAllSupportedEvents} from 'react-dom-bindings/src/events/DOMPluginEventSystem'; -import { - ELEMENT_NODE, - COMMENT_NODE, - DOCUMENT_NODE, - DOCUMENT_FRAGMENT_NODE, -} from 'react-dom-bindings/src/client/HTMLNodeType'; +import {COMMENT_NODE} from 'react-dom-bindings/src/client/HTMLNodeType'; import { createContainer, @@ -357,31 +352,6 @@ export function hydrateRoot( return new ReactDOMHydrationRoot(root); } -export function isValidContainer(node: any): boolean { - return !!( - node && - (node.nodeType === ELEMENT_NODE || - node.nodeType === DOCUMENT_NODE || - node.nodeType === DOCUMENT_FRAGMENT_NODE || - (!disableCommentsAsDOMContainers && - node.nodeType === COMMENT_NODE && - (node: any).nodeValue === ' react-mount-point-unstable ')) - ); -} - -// TODO: Remove this function which also includes comment nodes. -// We only use it in places that are currently more relaxed. -export function isValidContainerLegacy(node: any): boolean { - return !!( - node && - (node.nodeType === ELEMENT_NODE || - node.nodeType === DOCUMENT_NODE || - node.nodeType === DOCUMENT_FRAGMENT_NODE || - (node.nodeType === COMMENT_NODE && - (node: any).nodeValue === ' react-mount-point-unstable ')) - ); -} - function warnIfReactDOMContainerInDEV(container: any) { if (__DEV__) { if (isContainerMarkedAsRoot(container)) { diff --git a/packages/react-dom/src/client/ReactDOMRootFB.js b/packages/react-dom/src/client/ReactDOMRootFB.js index 38fe67c0a5b26..f41d8868279c7 100644 --- a/packages/react-dom/src/client/ReactDOMRootFB.js +++ b/packages/react-dom/src/client/ReactDOMRootFB.js @@ -36,7 +36,7 @@ import { unmarkContainerAsRoot, } from 'react-dom-bindings/src/client/ReactDOMComponentTree'; import {listenToAllSupportedEvents} from 'react-dom-bindings/src/events/DOMPluginEventSystem'; -import {isValidContainerLegacy} from './ReactDOMRoot'; +import {isValidContainerLegacy} from 'react-dom-bindings/src/client/ReactDOMContainer'; import { DOCUMENT_NODE, ELEMENT_NODE, diff --git a/packages/react-dom/src/server/ReactDOMServerRenderingStub.js b/packages/react-dom/src/server/ReactDOMServerRenderingStub.js deleted file mode 100644 index d3a7f33095fd6..0000000000000 --- a/packages/react-dom/src/server/ReactDOMServerRenderingStub.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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 { - preinit, - preinitModule, - preload, - preloadModule, - preconnect, - prefetchDNS, -} from '../shared/ReactDOMFloat'; -export { - useFormStatus, - useFormState, -} from 'react-dom-bindings/src/shared/ReactDOMFormActions'; - -export function createPortal() { - throw new Error( - 'createPortal was called on the server. Portals are not currently' + - ' supported on the server. Update your program to conditionally call' + - ' createPortal on the client only.', - ); -} - -export function flushSync() { - throw new Error( - 'flushSync was called on the server. This is likely caused by a' + - ' function being called during render or in module scope that was' + - ' intended to be called from an effect or event handler. Update your' + - ' to not call flushSync no the server.', - ); -} - -// on the server we just call the callback because there is -// not update mechanism. Really this should not be called on the -// server but since the semantics are generally clear enough we -// can provide this trivial implementation. -function batchedUpdates(fn: A => R, a: A): R { - return fn(a); -} - -export {batchedUpdates as unstable_batchedUpdates}; diff --git a/packages/react-dom/src/shared/ReactDOM.js b/packages/react-dom/src/shared/ReactDOM.js new file mode 100644 index 0000000000000..f1d48bfacd4de --- /dev/null +++ b/packages/react-dom/src/shared/ReactDOM.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) Meta Platforms, Inc. and 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 {ReactNodeList} from 'shared/ReactTypes'; + +import ReactVersion from 'shared/ReactVersion'; + +import {isValidContainer} from 'react-dom-bindings/src/client/ReactDOMContainer'; +import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal'; +import {flushSync} from './ReactDOMFlushSync'; + +import { + prefetchDNS, + preconnect, + preload, + preloadModule, + preinit, + preinitModule, +} from './ReactDOMFloat'; +import { + requestFormReset, + useFormStatus, + useFormState, +} from 'react-dom-bindings/src/shared/ReactDOMFormActions'; + +if (__DEV__) { + if ( + typeof Map !== 'function' || + // $FlowFixMe[prop-missing] Flow incorrectly thinks Map has no prototype + Map.prototype == null || + typeof Map.prototype.forEach !== 'function' || + typeof Set !== 'function' || + // $FlowFixMe[prop-missing] Flow incorrectly thinks Set has no prototype + Set.prototype == null || + typeof Set.prototype.clear !== 'function' || + typeof Set.prototype.forEach !== 'function' + ) { + console.error( + 'React depends on Map and Set built-in types. Make sure that you load a ' + + 'polyfill in older browsers. https://reactjs.org/link/react-polyfills', + ); + } +} + +function batchedUpdates(fn: (a: A) => R, a: A): R { + // batchedUpdates is now just a passthrough noop + return fn(a); +} + +function createPortal( + children: ReactNodeList, + container: Element | DocumentFragment, + key: ?string = null, +): React$Portal { + if (!isValidContainer(container)) { + throw new Error('Target container is not a DOM element.'); + } + + // TODO: pass ReactDOM portal implementation as third argument + // $FlowFixMe[incompatible-return] The Flow type is opaque but there's no way to actually create it. + return createPortalImpl(children, container, null, key); +} + +export { + ReactVersion as version, + createPortal, + flushSync, + batchedUpdates as unstable_batchedUpdates, + prefetchDNS, + preconnect, + preload, + preloadModule, + preinit, + preinitModule, + requestFormReset, + useFormStatus, + useFormState, +}; diff --git a/packages/react-dom/unstable_testing.classic.fb.js b/packages/react-dom/unstable_testing.classic.fb.js deleted file mode 100644 index db9a26388fb93..0000000000000 --- a/packages/react-dom/unstable_testing.classic.fb.js +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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, - findDOMNode, - flushSync, - render, - unmountComponentAtNode, - unstable_batchedUpdates, - unstable_createEventHandle, - unstable_renderSubtreeIntoContainer, - unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. - useFormStatus, - useFormState, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, - __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, -} from './index.classic.fb.js'; - -export {createRoot, hydrateRoot} from './client.js'; - -export { - createComponentSelector, - createHasPseudoClassSelector, - createRoleSelector, - createTestNameSelector, - createTextSelector, - getFindAllNodesFailureDescription, - findAllNodes, - findBoundingRects, - focusWithin, - observeVisibleRects, -} from 'react-reconciler/src/ReactFiberReconciler'; diff --git a/packages/react-dom/unstable_testing.experimental.js b/packages/react-dom/unstable_testing.experimental.js index da7aeb1fb1a92..499635551ed58 100644 --- a/packages/react-dom/unstable_testing.experimental.js +++ b/packages/react-dom/unstable_testing.experimental.js @@ -7,23 +7,7 @@ * @flow */ -export { - createPortal, - flushSync, - unstable_batchedUpdates, - useFormStatus, - useFormState, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, - __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, -} from './index.experimental.js'; - -export {createRoot, hydrateRoot} from './client.js'; +export * from './client.js'; export { createComponentSelector, diff --git a/packages/react-dom/unstable_testing.js b/packages/react-dom/unstable_testing.js index 19cc1515cdffe..bc330f3537ef3 100644 --- a/packages/react-dom/unstable_testing.js +++ b/packages/react-dom/unstable_testing.js @@ -7,34 +7,4 @@ * @flow */ -export { - createPortal, - flushSync, - unstable_batchedUpdates, - unstable_createEventHandle, - unstable_runWithPriority, // DO NOT USE: Temporarily exposed to migrate off of Scheduler.runWithPriority. - useFormStatus, - useFormState, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, -} from './index.js'; - -export {createRoot, hydrateRoot} from './client.js'; - -export { - createComponentSelector, - createHasPseudoClassSelector, - createRoleSelector, - createTestNameSelector, - createTextSelector, - getFindAllNodesFailureDescription, - findAllNodes, - findBoundingRects, - focusWithin, - observeVisibleRects, -} from 'react-reconciler/src/ReactFiberReconciler'; +export * from './client.js'; diff --git a/packages/react-dom/unstable_testing.stable.js b/packages/react-dom/unstable_testing.stable.js deleted file mode 100644 index 59b667fa15a7d..0000000000000 --- a/packages/react-dom/unstable_testing.stable.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and 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, - flushSync, - unstable_batchedUpdates, - useFormStatus, - useFormState, - prefetchDNS, - preconnect, - preload, - preloadModule, - preinit, - preinitModule, - version, - __DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, -} from './index.stable.js'; -export {createRoot, hydrateRoot} from './client.js'; diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index c92f7799bb6e8..58833f3654a5d 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -1172,19 +1172,22 @@ describe('ReactFlightDOMBrowser', () => { root.render(); }); expect(document.head.innerHTML).toBe( - // Currently the react-dom entrypoint loads the fiber implementation - // even if you never pull in the the client APIs. this causes the fiber - // dispatcher to be present even for Flight ReactDOM calls. This is not what - // you would have in a real application but given we're runnign flight and - // fiber the in the same scope it's unavoidable until we make the entrypoint - // not automatically pull in the fiber implementation. This test currently - // asserts this be demonstrating that the preload call after the await point - // is written to the document before the call before it. We still demonstrate that - // flight handled the sync call because if the fiber implementation did it would appear - // before the after call. In the future we will change this assertion once the fiber - // implementation no long automatically gets pulled in - '', - // '', + gate(f => f.www) + ? // The www entrypoints for ReactDOM and ReactDOMClient are unified so even + // when you pull in just the top level the dispatcher for the Document is + // loaded alongside it. In a normal environment there would be nothing to dispatch to + // in a server environment so the preload calls would still only be dispatched to fizz + // or the browser but not both. However in this contrived test environment the preloads + // are being dispatched simultaneously causing an extraneous preload to show up. This test currently + // asserts this be demonstrating that the preload call after the await point + // is written to the document before the call before it. We still demonstrate that + // flight handled the sync call because if the fiber implementation did it would appear + // before the after call. In the future we will change this assertion once the fiber + // implementation no long automatically gets pulled in + '' + : // For other release channels the client and isomorphic entrypoints are separate and thus we only + // observe the expected preload from before the first await + '', ); expect(container.innerHTML).toBe('

hello world

'); }); diff --git a/scripts/jest/setupHostConfigs.js b/scripts/jest/setupHostConfigs.js index 8f2d508856e6f..0339f144693e2 100644 --- a/scripts/jest/setupHostConfigs.js +++ b/scripts/jest/setupHostConfigs.js @@ -12,8 +12,43 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { // .stable.js // .experimental.js // .js - if (isFBBundle) { + // FB builds for react-dom need to alias both react-dom and react-dom/client to the same + // entrypoint since there is only a single build for them. + if ( + resolvedEntry.endsWith('react-dom/index.js') || + resolvedEntry.endsWith('react-dom/client.js') || + resolvedEntry.endsWith('react-dom/unstable_testing.js') + ) { + let specifier; + let entrypoint; + if (resolvedEntry.endsWith('index.js')) { + specifier = 'react-dom'; + entrypoint = __EXPERIMENTAL__ + ? 'src/ReactDOMFB.modern.js' + : 'src/ReactDOMFB.js'; + } else if (resolvedEntry.endsWith('client.js')) { + specifier = 'react-dom/client'; + entrypoint = __EXPERIMENTAL__ + ? 'src/ReactDOMFB.modern.js' + : 'src/ReactDOMFB.js'; + } else { + // must be unstable_testing + specifier = 'react-dom/unstable_testing'; + entrypoint = __EXPERIMENTAL__ + ? 'src/ReactDOMTestingFB.modern.js' + : 'src/ReactDOMTestingFB.js'; + } + + resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint); + if (fs.existsSync(resolvedEntry)) { + return resolvedEntry; + } + const fbReleaseChannel = __EXPERIMENTAL__ ? 'www-modern' : 'www-classic'; + throw new Error( + `${fbReleaseChannel} tests are expected to alias ${specifier} to ${entrypoint} but this file was not found` + ); + } const resolvedFBEntry = resolvedEntry.replace( '.js', __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index 6e7ed4092de44..c27e8bcc54cfd 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -163,14 +163,7 @@ const bundles = [ /******* React DOM *******/ { - bundleTypes: [ - NODE_DEV, - NODE_PROD, - NODE_PROFILING, - FB_WWW_DEV, - FB_WWW_PROD, - FB_WWW_PROFILING, - ], + bundleTypes: [NODE_DEV, NODE_PROD], moduleType: RENDERER, entry: 'react-dom', global: 'ReactDOM', @@ -178,12 +171,43 @@ const bundles = [ wrapWithModuleBoundaries: true, externals: ['react'], }, + /******* React DOM Client *******/ + { + bundleTypes: [NODE_DEV, NODE_PROD], + moduleType: RENDERER, + entry: 'react-dom/client', + global: 'ReactDOM', + minifyWithProdErrorCodes: true, + wrapWithModuleBoundaries: true, + externals: ['react', 'react-dom'], + }, + + /******* React DOM Profiling (Client) *******/ + { + bundleTypes: [NODE_DEV, NODE_PROFILING], + moduleType: RENDERER, + entry: 'react-dom/profiling', + global: 'ReactDOM', + minifyWithProdErrorCodes: true, + wrapWithModuleBoundaries: true, + externals: ['react', 'react-dom'], + }, + /******* React DOM FB *******/ + { + bundleTypes: [FB_WWW_DEV, FB_WWW_PROD, FB_WWW_PROFILING], + moduleType: RENDERER, + entry: 'react-dom/src/ReactDOMFB.js', + global: 'ReactDOM', + minifyWithProdErrorCodes: true, + wrapWithModuleBoundaries: true, + externals: ['react'], + }, /******* React DOM React Server *******/ { bundleTypes: [NODE_DEV, NODE_PROD], moduleType: RENDERER, - entry: 'react-dom/src/ReactDOMServer.js', + entry: 'react-dom/src/ReactDOMReactServer.js', name: 'react-dom.react-server', condition: 'react-server', global: 'ReactDOM', @@ -203,16 +227,25 @@ const bundles = [ externals: ['react', 'react-dom'], }, - /******* React DOM - www - Testing *******/ + /******* React DOM - Testing *******/ { moduleType: RENDERER, - bundleTypes: __EXPERIMENTAL__ - ? [FB_WWW_DEV, FB_WWW_PROD, NODE_DEV, NODE_PROD] - : [FB_WWW_DEV, FB_WWW_PROD], + bundleTypes: __EXPERIMENTAL__ ? [NODE_DEV, NODE_PROD] : [], entry: 'react-dom/unstable_testing', global: 'ReactDOMTesting', minifyWithProdErrorCodes: true, wrapWithModuleBoundaries: false, + externals: ['react', 'react-dom'], + }, + + /******* React DOM - www - Testing *******/ + { + moduleType: RENDERER, + bundleTypes: [FB_WWW_DEV, FB_WWW_PROD], + entry: 'react-dom/src/ReactDOMTestingFB.js', + global: 'ReactDOMTesting', + minifyWithProdErrorCodes: true, + wrapWithModuleBoundaries: false, externals: ['react'], }, @@ -318,18 +351,6 @@ const bundles = [ externals: [], }, - /******* React DOM Server Render Stub *******/ - { - bundleTypes: [NODE_DEV, NODE_PROD], - moduleType: RENDERER, - entry: 'react-dom/server-rendering-stub', - name: 'react-dom-server-rendering-stub', - global: 'ReactDOMServerRenderingStub', - minifyWithProdErrorCodes: true, - wrapWithModuleBoundaries: false, - externals: ['react'], - }, - /******* React Server DOM Webpack Server *******/ { bundleTypes: [NODE_DEV, NODE_PROD], diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index 98449cf8f84b0..73c0acf9c506a 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -94,9 +94,9 @@ const forks = Object.freeze({ ) => { if ( entry === 'react-dom' || - entry === 'react-dom/server-rendering-stub' || - entry === 'react-dom/src/ReactDOMServer.js' || - entry === 'react-dom/unstable_testing' + entry === 'react-dom/src/ReactDOMFB.js' || + entry === 'react-dom/src/ReactDOMTestingFB.js' || + entry === 'react-dom/src/ReactDOMServer.js' ) { if ( bundleType === FB_WWW_DEV || diff --git a/scripts/rollup/modules.js b/scripts/rollup/modules.js index 6e8a0c9f0b439..e1ebc1c945a0d 100644 --- a/scripts/rollup/modules.js +++ b/scripts/rollup/modules.js @@ -29,7 +29,6 @@ const knownGlobals = Object.freeze({ react: 'React', 'react-dom': 'ReactDOM', 'react-dom/server': 'ReactDOMServer', - 'react-interactions/events/tap': 'ReactEventsTap', scheduler: 'Scheduler', 'scheduler/unstable_mock': 'SchedulerMock', ReactNativeInternalFeatureFlags: 'ReactNativeInternalFeatureFlags', diff --git a/scripts/shared/inlinedHostConfigs.js b/scripts/shared/inlinedHostConfigs.js index 63ec4db4dbe39..6e1631bbde330 100644 --- a/scripts/shared/inlinedHostConfigs.js +++ b/scripts/shared/inlinedHostConfigs.js @@ -11,21 +11,23 @@ module.exports = [ shortName: 'dom-node', entryPoints: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/client', + 'react-dom/profiling', 'react-dom/unstable_testing', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom/src/server/react-dom-server.node.js', 'react-dom/static.node', 'react-dom/test-utils', - 'react-dom/server-rendering-stub', 'react-dom/unstable_server-external-runtime', 'react-server-dom-webpack/server.node.unbundled', 'react-server-dom-webpack/client.node.unbundled', ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom/static', @@ -45,7 +47,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', 'react-server/src/ReactFlightServerConfigDebugNode.js', ], @@ -62,6 +63,7 @@ module.exports = [ 'react-dom', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom/static', @@ -82,7 +84,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', 'react-server/src/ReactFlightServerConfigDebugNode.js', ], @@ -99,6 +100,7 @@ module.exports = [ 'react-dom', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom/static', @@ -119,7 +121,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', 'react-server/src/ReactFlightServerConfigDebugNode.js', ], @@ -136,6 +137,7 @@ module.exports = [ 'react-dom', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom/static', @@ -157,7 +159,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', 'react-server/src/ReactFlightServerConfigDebugNode.js', ], @@ -166,9 +167,17 @@ module.exports = [ }, { shortName: 'dom-bun', - entryPoints: ['react-dom', 'react-dom/src/server/react-dom-server.bun.js'], + entryPoints: [ + 'react-dom', + 'react-dom/client', + 'react-dom/profiling', + 'react-dom/unstable_testing', + 'react-dom/src/server/react-dom-server.bun.js', + ], paths: [ 'react-dom', + 'react-dom/client', + 'react-dom/profiling', 'react-dom/server.bun', 'react-dom/src/server/react-dom-server.bun', 'react-dom/src/server/ReactDOMFizzServerBun.js', @@ -182,19 +191,21 @@ module.exports = [ shortName: 'dom-browser', entryPoints: [ 'react-dom', + 'react-dom/client', + 'react-dom/profiling', 'react-dom/unstable_testing', 'react-dom/src/server/react-dom-server.browser.js', 'react-dom/static.browser', - 'react-dom/server-rendering-stub', 'react-dom/unstable_server-external-runtime', 'react-server-dom-webpack/server.browser', 'react-server-dom-webpack/client.browser', ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server.browser', 'react-dom/static.browser', 'react-dom/unstable_testing', @@ -223,8 +234,9 @@ module.exports = [ entryPoints: ['react-server-dom-esm/client.browser'], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom-bindings', @@ -236,7 +248,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', ], isFlowTyped: true, @@ -251,6 +262,7 @@ module.exports = [ paths: [ 'react-dom', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom-bindings', @@ -266,7 +278,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', ], isFlowTyped: true, @@ -282,9 +293,10 @@ module.exports = [ ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server.edge', 'react-dom/static.edge', 'react-dom/unstable_testing', @@ -316,9 +328,10 @@ module.exports = [ ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server.edge', 'react-dom/static.edge', 'react-dom/unstable_testing', @@ -350,9 +363,10 @@ module.exports = [ ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-dom/client', + 'react-dom/profiling', 'react-dom/server', 'react-dom/server.node', 'react-dom/static', @@ -370,7 +384,6 @@ module.exports = [ 'react-devtools-core', 'react-devtools-shell', 'react-devtools-shared', - 'react-interactions', 'shared/ReactDOMSharedInternals', 'react-server/src/ReactFlightServerConfigDebugNode.js', ], @@ -385,7 +398,7 @@ module.exports = [ ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-server-dom-webpack', 'react-dom/src/server/ReactDOMLegacyServerImpl.js', // not an entrypoint, but only usable in *Browser and *Node files @@ -400,10 +413,16 @@ module.exports = [ }, { shortName: 'dom-fb', - entryPoints: ['react-server-dom-fb/src/ReactDOMServerFB.js'], + entryPoints: [ + 'react-dom/src/ReactDOMFB.js', + 'react-dom/src/ReactDOMTestingFB.js', + 'react-server-dom-fb/src/ReactDOMServerFB.js', + ], paths: [ 'react-dom', - 'react-dom/src/ReactDOMServer.js', + 'react-dom/src/ReactDOMFB.js', + 'react-dom/src/ReactDOMTestingFB.js', + 'react-dom/src/ReactDOMReactServer.js', 'react-dom-bindings', 'react-server-dom-fb/src/ReactDOMServerFB.js', 'shared/ReactDOMSharedInternals', diff --git a/scripts/shared/pathsByLanguageVersion.js b/scripts/shared/pathsByLanguageVersion.js index 10c49821af5e9..4a754f3cd491e 100644 --- a/scripts/shared/pathsByLanguageVersion.js +++ b/scripts/shared/pathsByLanguageVersion.js @@ -16,8 +16,6 @@ const esNextPaths = [ // Source files 'packages/*/src/**/*.js', 'packages/dom-event-testing-library/**/*.js', - 'packages/react-interactions/**/*.js', - 'packages/react-interactions/**/*.js', 'packages/shared/**/*.js', // Shims and Flow environment 'scripts/flow/*.js',