Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -779,13 +779,5 @@ function runActTests(render, unmount, rerender) {
});
}
});
describe('throw in prod mode', () => {
Copy link
Collaborator Author

@eps1lon eps1lon Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced by the test in packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js. This is testing React.act not react-dom/test-utils

// @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',
);
});
});
});
}
12 changes: 12 additions & 0 deletions packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ describe('isomorphic act()', () => {
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__
it('bypasses queueMicrotask', async () => {
const root = ReactNoop.createRoot();
Expand Down
10 changes: 10 additions & 0 deletions packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
it('can get the component owner stacks during rendering in dev', async () => {
let stack;
Expand Down
2 changes: 0 additions & 2 deletions packages/react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ export type ElementRef<C> = React$ElementRef<C>;
export type Config<Props, DefaultProps> = React$Config<Props, DefaultProps>;
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 {
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
__COMPILER_RUNTIME,
Expand Down
51 changes: 51 additions & 0 deletions packages/react/index.stable.development.js
Original file line number Diff line number Diff line change
@@ -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';
1 change: 0 additions & 1 deletion packages/react/index.stable.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
export {
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
__COMPILER_RUNTIME,
act,
Children,
Component,
Fragment,
Expand Down
1 change: 1 addition & 0 deletions scripts/jest/TestFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ function getTestFlags() {
classic: releaseChannel === 'classic',
source: !process.env.IS_BUILD,
www,
fb: www || xplat,

// These aren't flags, just a useful aliases for tests.
enableActivity: releaseChannel === 'experimental' || www || xplat,
Expand Down
19 changes: 13 additions & 6 deletions scripts/jest/setupHostConfigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand All @@ -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.
Expand Down
21 changes: 9 additions & 12 deletions scripts/rollup/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ function shouldSkipBundle(bundle, bundleType) {
return false;
}

function resolveEntryFork(resolvedEntry, isFBBundle) {
function resolveEntryFork(resolvedEntry, isFBBundle, isDev) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Third arg was always passed but not utilized until now.

// Pick which entry point fork to use:
// .modern.fb.js
// .classic.fb.js
Expand All @@ -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;
Expand All @@ -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)) {
Expand All @@ -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)
Expand Down
Loading