Stop exporting dev-only methods in OSS production builds (#32200)

This commit is contained in:
Sebastian "Sebbie" Silbermann
2025-02-13 18:26:36 +01:00
committed by GitHub
parent c6a7e18636
commit ed8b68dd17
9 changed files with 96 additions and 29 deletions

View File

@@ -779,13 +779,5 @@ 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',
);
});
});
}); });
} }

View File

@@ -50,6 +50,18 @@ describe('isomorphic act()', () => {
return text; return text;
} }
it('behavior in production', () => {
if (!__DEV__) {
if (gate('fb')) {
expect(() => act(() => {})).toThrow(
'act(...) is not supported in production builds of React',
);
} else {
expect(React).not.toHaveProperty('act');
}
}
});
// @gate __DEV__ // @gate __DEV__
it('bypasses queueMicrotask', async () => { it('bypasses queueMicrotask', async () => {
const root = ReactNoop.createRoot(); const root = ReactNoop.createRoot();

View File

@@ -31,6 +31,16 @@ describe('ReactOwnerStacks', () => {
); );
} }
it('behavior in production', () => {
if (!__DEV__) {
if (gate('fb')) {
expect(React).toHaveProperty('captureOwnerStack', undefined);
} else {
expect(React).not.toHaveProperty('captureOwnerStack');
}
}
});
// @gate __DEV__ && enableOwnerStacks // @gate __DEV__ && enableOwnerStacks
it('can get the component owner stacks during rendering in dev', async () => { it('can get the component owner stacks during rendering in dev', async () => {
let stack; let stack;

View File

@@ -23,8 +23,6 @@ export type ElementRef<C> = React$ElementRef<C>;
export type Config<Props, DefaultProps> = React$Config<Props, DefaultProps>; export type Config<Props, DefaultProps> = React$Config<Props, DefaultProps>;
export type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T; export type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T;
// Export all exports so that they're available in tests.
// We can't use export * from in Flow for some reason.
export { export {
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
__COMPILER_RUNTIME, __COMPILER_RUNTIME,

View File

@@ -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';

View File

@@ -10,7 +10,6 @@
export { export {
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
__COMPILER_RUNTIME, __COMPILER_RUNTIME,
act,
Children, Children,
Component, Component,
Fragment, Fragment,

View File

@@ -78,6 +78,7 @@ function getTestFlags() {
classic: releaseChannel === 'classic', classic: releaseChannel === 'classic',
source: !process.env.IS_BUILD, source: !process.env.IS_BUILD,
www, www,
fb: www || xplat,
// These aren't flags, just a useful aliases for tests. // These aren't flags, just a useful aliases for tests.
enableActivity: releaseChannel === 'experimental' || www || xplat, enableActivity: releaseChannel === 'experimental' || www || xplat,

View File

@@ -43,9 +43,9 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
} }
resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint); resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint);
const developmentEntry = resolvedEntry.replace('.js', '.development.js'); const devEntry = resolvedEntry.replace('.js', '.development.js');
if (fs.existsSync(developmentEntry)) { if (__DEV__ && fs.existsSync(devEntry)) {
return developmentEntry; return devEntry;
} }
if (fs.existsSync(resolvedEntry)) { if (fs.existsSync(resolvedEntry)) {
return resolvedEntry; return resolvedEntry;
@@ -60,13 +60,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
__EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js'
); );
const devFBEntry = resolvedFBEntry.replace('.js', '.development.js'); const devFBEntry = resolvedFBEntry.replace('.js', '.development.js');
if (fs.existsSync(devFBEntry)) { if (__DEV__ && fs.existsSync(devFBEntry)) {
return devFBEntry; return devFBEntry;
} }
if (fs.existsSync(resolvedFBEntry)) { if (fs.existsSync(resolvedFBEntry)) {
return resolvedFBEntry; return resolvedFBEntry;
} }
const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js'); 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)) { if (fs.existsSync(resolvedGenericFBEntry)) {
return resolvedGenericFBEntry; return resolvedGenericFBEntry;
} }
@@ -77,14 +84,14 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
__EXPERIMENTAL__ ? '.experimental.js' : '.stable.js' __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js'
); );
const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js'); const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js');
if (fs.existsSync(devForkedEntry)) { if (__DEV__ && fs.existsSync(devForkedEntry)) {
return devForkedEntry; return devForkedEntry;
} }
if (fs.existsSync(resolvedForkedEntry)) { if (fs.existsSync(resolvedForkedEntry)) {
return resolvedForkedEntry; return resolvedForkedEntry;
} }
const plainDevEntry = resolvedEntry.replace('.js', '.development.js'); const plainDevEntry = resolvedEntry.replace('.js', '.development.js');
if (fs.existsSync(plainDevEntry)) { if (__DEV__ && fs.existsSync(plainDevEntry)) {
return plainDevEntry; return plainDevEntry;
} }
// Just use the plain .js one. // Just use the plain .js one.

View File

@@ -571,7 +571,7 @@ function shouldSkipBundle(bundle, bundleType) {
return false; return false;
} }
function resolveEntryFork(resolvedEntry, isFBBundle) { function resolveEntryFork(resolvedEntry, isFBBundle, isDev) {
// Pick which entry point fork to use: // Pick which entry point fork to use:
// .modern.fb.js // .modern.fb.js
// .classic.fb.js // .classic.fb.js
@@ -586,23 +586,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
'.js', '.js',
__EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js' __EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js'
); );
const developmentFBEntry = resolvedFBEntry.replace( const devFBEntry = resolvedFBEntry.replace('.js', '.development.js');
'.js', if (isDev && fs.existsSync(devFBEntry)) {
'.development.js' return devFBEntry;
);
if (fs.existsSync(developmentFBEntry)) {
return developmentFBEntry;
} }
if (fs.existsSync(resolvedFBEntry)) { if (fs.existsSync(resolvedFBEntry)) {
return resolvedFBEntry; return resolvedFBEntry;
} }
const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js'); const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js');
const developmentGenericFBEntry = resolvedGenericFBEntry.replace( const devGenericFBEntry = resolvedGenericFBEntry.replace(
'.js', '.js',
'.development.js' '.development.js'
); );
if (fs.existsSync(developmentGenericFBEntry)) { if (isDev && fs.existsSync(devGenericFBEntry)) {
return developmentGenericFBEntry; return devGenericFBEntry;
} }
if (fs.existsSync(resolvedGenericFBEntry)) { if (fs.existsSync(resolvedGenericFBEntry)) {
return resolvedGenericFBEntry; return resolvedGenericFBEntry;
@@ -614,7 +611,7 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
__EXPERIMENTAL__ ? '.experimental.js' : '.stable.js' __EXPERIMENTAL__ ? '.experimental.js' : '.stable.js'
); );
const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js'); const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js');
if (fs.existsSync(devForkedEntry)) { if (isDev && fs.existsSync(devForkedEntry)) {
return devForkedEntry; return devForkedEntry;
} }
if (fs.existsSync(resolvedForkedEntry)) { if (fs.existsSync(resolvedForkedEntry)) {
@@ -633,7 +630,7 @@ async function createBundle(bundle, bundleType) {
const {isFBWWWBundle, isFBRNBundle} = getBundleTypeFlags(bundleType); const {isFBWWWBundle, isFBRNBundle} = getBundleTypeFlags(bundleType);
let resolvedEntry = resolveEntryFork( const resolvedEntry = resolveEntryFork(
require.resolve(bundle.entry), require.resolve(bundle.entry),
isFBWWWBundle || isFBRNBundle, isFBWWWBundle || isFBRNBundle,
!isProductionBundleType(bundleType) !isProductionBundleType(bundleType)