From 6a60f6d4a01a1b4e13c5e66191a234bb29c5d2df Mon Sep 17 00:00:00 2001 From: Sebastian Sebbie Silbermann Date: Thu, 23 Jan 2025 17:12:21 +0100 Subject: [PATCH] Stop exporting dev-only methods production builds --- .../src/__tests__/ReactTestUtilsAct-test.js | 11 +-- .../src/__tests__/ReactIsomorphicAct-test.js | 5 ++ .../src/__tests__/ReactOwnerStacks-test.js | 5 ++ packages/react/index.fb.development.js | 79 +++++++++++++++++++ packages/react/index.fb.js | 11 --- packages/react/index.js | 2 - packages/react/index.stable.development.js | 51 ++++++++++++ packages/react/index.stable.js | 1 - scripts/jest/setupHostConfigs.js | 19 +++-- scripts/rollup/build.js | 21 +++-- 10 files changed, 166 insertions(+), 39 deletions(-) create mode 100644 packages/react/index.fb.development.js create mode 100644 packages/react/index.stable.development.js diff --git a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js index 76cfab25e0016c..dd4030866e0595 100644 --- a/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js +++ b/packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js @@ -779,13 +779,10 @@ function runActTests(render, unmount, rerender) { }); } }); - describe('throw in prod mode', () => { - // @gate !__DEV__ - it('warns if you try to use act() in prod mode', () => { - expect(() => act(() => {})).toThrow( - 'act(...) is not supported in production builds of React', - ); - }); + + // @gate !__DEV__ + it('is not available in prod mode', () => { + expect(() => act(() => {})).toThrow('act is not a function'); }); }); } diff --git a/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js b/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js index eeec3b2b7eb3b0..639ca0d5bd9890 100644 --- a/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js @@ -50,6 +50,11 @@ describe('isomorphic act()', () => { return text; } + // @gate !__DEV__ + it('is not exported in production builds', () => { + expect(React).not.toHaveProperty('act'); + }); + // @gate __DEV__ it('bypasses queueMicrotask', async () => { const root = ReactNoop.createRoot(); diff --git a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js index ca0e9fd12cc119..fb4ce034412849 100644 --- a/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js +++ b/packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js @@ -31,6 +31,11 @@ describe('ReactOwnerStacks', () => { ); } + // @gate !__DEV__ || !enableOwnerStacks + it('is not exported in production buids', () => { + expect(React).not.toHaveProperty('captureOwnerStack'); + }); + // @gate __DEV__ && enableOwnerStacks it('can get the component owner stacks during rendering in dev', async () => { let stack; diff --git a/packages/react/index.fb.development.js b/packages/react/index.fb.development.js new file mode 100644 index 00000000000000..ec819a0b31ad55 --- /dev/null +++ b/packages/react/index.fb.development.js @@ -0,0 +1,79 @@ +/** + * 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 {enableOwnerStacks} from 'shared/ReactFeatureFlags'; +import {captureOwnerStack as captureOwnerStackImpl} from './src/ReactClient'; + +export { + __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, + cache, + Children, + cloneElement, + Component, + createContext, + createElement, + createRef, + experimental_useEffectEvent, + experimental_useResourceEffect, + forwardRef, + Fragment, + isValidElement, + lazy, + memo, + Profiler, + PureComponent, + startTransition, + StrictMode, + Suspense, + unstable_Activity, + unstable_getCacheForType, + unstable_LegacyHidden, + unstable_Scope, + unstable_SuspenseList, + unstable_TracingMarker, + unstable_useCacheRefresh, + use, + useActionState, + useCallback, + useContext, + useDebugValue, + useDeferredValue, + useEffect, + useId, + useImperativeHandle, + useInsertionEffect, + useLayoutEffect, + useMemo, + useOptimistic, + useReducer, + useRef, + useState, + useSyncExternalStore, + useTransition, + version, + act, // DEV-only +} from './src/ReactClient'; + +export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX'; + +// export for backwards compatibility during upgrade +export {useMemoCache as unstable_useMemoCache} from './src/ReactHooks'; + +// export to match the name of the OSS function typically exported from +// react/compiler-runtime +export {useMemoCache as c} from './src/ReactHooks'; + +// Only export captureOwnerStack if enabled. +let captureOwnerStack: ?() => null | string; +if (enableOwnerStacks) { + captureOwnerStack = captureOwnerStackImpl; +} + +// DEV-only +export {captureOwnerStack}; diff --git a/packages/react/index.fb.js b/packages/react/index.fb.js index 84128cf0ea5d9e..322b85b7f69eea 100644 --- a/packages/react/index.fb.js +++ b/packages/react/index.fb.js @@ -7,12 +7,9 @@ * @flow */ -import {enableOwnerStacks} from 'shared/ReactFeatureFlags'; -import {captureOwnerStack as captureOwnerStackImpl} from './src/ReactClient'; export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __COMPILER_RUNTIME, - act, cache, Children, cloneElement, @@ -68,11 +65,3 @@ export {useMemoCache as unstable_useMemoCache} from './src/ReactHooks'; // export to match the name of the OSS function typically exported from // react/compiler-runtime export {useMemoCache as c} from './src/ReactHooks'; - -// Only export captureOwnerStack in development. -let captureOwnerStack: ?() => null | string; -if (__DEV__ && enableOwnerStacks) { - captureOwnerStack = captureOwnerStackImpl; -} - -export {captureOwnerStack}; diff --git a/packages/react/index.js b/packages/react/index.js index 711a0444552574..af4cd4b0c1b17c 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -23,8 +23,6 @@ export type ElementRef = React$ElementRef; export type Config = React$Config; export type ChildrenArray<+T> = $ReadOnlyArray> | T; -// Export all exports so that they're available in tests. -// We can't use export * from in Flow for some reason. export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __COMPILER_RUNTIME, diff --git a/packages/react/index.stable.development.js b/packages/react/index.stable.development.js new file mode 100644 index 00000000000000..f9ac98bafbf582 --- /dev/null +++ b/packages/react/index.stable.development.js @@ -0,0 +1,51 @@ +/** + * 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 { + __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, + __COMPILER_RUNTIME, + Children, + Component, + Fragment, + Profiler, + PureComponent, + StrictMode, + Suspense, + cloneElement, + createContext, + createElement, + createRef, + use, + forwardRef, + isValidElement, + lazy, + memo, + cache, + unstable_useCacheRefresh, + startTransition, + useId, + useCallback, + useContext, + useDebugValue, + useDeferredValue, + useEffect, + useImperativeHandle, + useInsertionEffect, + useLayoutEffect, + useMemo, + useReducer, + useOptimistic, + useRef, + useState, + useSyncExternalStore, + useTransition, + useActionState, + version, + act, // DEV-only +} from './src/ReactClient'; diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index 26e22c343223e0..6f25c7a37d9836 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -10,7 +10,6 @@ export { __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __COMPILER_RUNTIME, - act, Children, Component, Fragment, diff --git a/scripts/jest/setupHostConfigs.js b/scripts/jest/setupHostConfigs.js index b52fac3201ebfb..aacfaf116fcd2f 100644 --- a/scripts/jest/setupHostConfigs.js +++ b/scripts/jest/setupHostConfigs.js @@ -43,9 +43,9 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { } resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint); - const developmentEntry = resolvedEntry.replace('.js', '.development.js'); - if (fs.existsSync(developmentEntry)) { - return developmentEntry; + const devEntry = resolvedEntry.replace('.js', '.development.js'); + if (__DEV__ && fs.existsSync(devEntry)) { + return devEntry; } if (fs.existsSync(resolvedEntry)) { return resolvedEntry; @@ -60,13 +60,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' ); const devFBEntry = resolvedFBEntry.replace('.js', '.development.js'); - if (fs.existsSync(devFBEntry)) { + if (__DEV__ && fs.existsSync(devFBEntry)) { return devFBEntry; } if (fs.existsSync(resolvedFBEntry)) { return resolvedFBEntry; } const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js'); + const devGenericFBEntry = resolvedGenericFBEntry.replace( + '.js', + '.development.js' + ); + if (__DEV__ && fs.existsSync(devGenericFBEntry)) { + return devGenericFBEntry; + } if (fs.existsSync(resolvedGenericFBEntry)) { return resolvedGenericFBEntry; } @@ -77,14 +84,14 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js' ); const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js'); - if (fs.existsSync(devForkedEntry)) { + if (__DEV__ && fs.existsSync(devForkedEntry)) { return devForkedEntry; } if (fs.existsSync(resolvedForkedEntry)) { return resolvedForkedEntry; } const plainDevEntry = resolvedEntry.replace('.js', '.development.js'); - if (fs.existsSync(plainDevEntry)) { + if (__DEV__ && fs.existsSync(plainDevEntry)) { return plainDevEntry; } // Just use the plain .js one. diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index 2a67c13e1e620b..547362dae7123f 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -571,7 +571,7 @@ function shouldSkipBundle(bundle, bundleType) { return false; } -function resolveEntryFork(resolvedEntry, isFBBundle) { +function resolveEntryFork(resolvedEntry, isFBBundle, isDev) { // Pick which entry point fork to use: // .modern.fb.js // .classic.fb.js @@ -586,23 +586,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { '.js', __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' ); - const developmentFBEntry = resolvedFBEntry.replace( - '.js', - '.development.js' - ); - if (fs.existsSync(developmentFBEntry)) { - return developmentFBEntry; + const devFBEntry = resolvedFBEntry.replace('.js', '.development.js'); + if (isDev && fs.existsSync(devFBEntry)) { + return devFBEntry; } if (fs.existsSync(resolvedFBEntry)) { return resolvedFBEntry; } const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js'); - const developmentGenericFBEntry = resolvedGenericFBEntry.replace( + const devGenericFBEntry = resolvedGenericFBEntry.replace( '.js', '.development.js' ); - if (fs.existsSync(developmentGenericFBEntry)) { - return developmentGenericFBEntry; + if (isDev && fs.existsSync(devGenericFBEntry)) { + return devGenericFBEntry; } if (fs.existsSync(resolvedGenericFBEntry)) { return resolvedGenericFBEntry; @@ -614,7 +611,7 @@ function resolveEntryFork(resolvedEntry, isFBBundle) { __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js' ); const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js'); - if (fs.existsSync(devForkedEntry)) { + if (isDev && fs.existsSync(devForkedEntry)) { return devForkedEntry; } if (fs.existsSync(resolvedForkedEntry)) { @@ -633,7 +630,7 @@ async function createBundle(bundle, bundleType) { const {isFBWWWBundle, isFBRNBundle} = getBundleTypeFlags(bundleType); - let resolvedEntry = resolveEntryFork( + const resolvedEntry = resolveEntryFork( require.resolve(bundle.entry), isFBWWWBundle || isFBRNBundle, !isProductionBundleType(bundleType)