From d5fce8044a6781751bd535263df458ac07c89d9f 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__/ReactIsomorphicAct-test.js | 5 ++ .../src/__tests__/ReactOwnerStacks-test.js | 5 ++ packages/react/index.js | 2 - packages/react/index.stable.development.js | 74 +++++++++++++++++++ packages/react/index.stable.js | 1 - scripts/jest/setupHostConfigs.js | 12 +-- scripts/rollup/build.js | 21 +++--- 7 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 packages/react/index.stable.development.js 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..a8ca948d00d2e2 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__ + 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.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..c3883f0623dd78 --- /dev/null +++ b/packages/react/index.stable.development.js @@ -0,0 +1,74 @@ +/** + * 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, + startTransition, + unstable_Activity, + unstable_postpone, + unstable_getCacheForType, + unstable_SuspenseList, + unstable_ViewTransition, + unstable_addTransitionType, + unstable_useCacheRefresh, + useId, + useCallback, + useContext, + useDebugValue, + useDeferredValue, + useEffect, + experimental_useEffectEvent, + experimental_useResourceEffect, + useImperativeHandle, + useInsertionEffect, + useLayoutEffect, + useMemo, + useOptimistic, + useReducer, + useRef, + useState, + useSyncExternalStore, + useTransition, + useActionState, + version, + act, // DEV-only +} from './src/ReactClient'; + +import {useOptimistic} from './src/ReactClient'; + +export function experimental_useOptimistic( + passthrough: S, + reducer: ?(S, A) => S, +): [S, (A) => void] { + if (__DEV__) { + console.error( + 'useOptimistic is now in canary. Remove the experimental_ prefix. ' + + 'The prefixed alias will be removed in an upcoming release.', + ); + } + return useOptimistic(passthrough, reducer); +} 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..3642588f1b860f 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,7 +60,7 @@ 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)) { @@ -77,14 +77,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)