Stop exporting dev-only methods in OSS production builds (#32200)
Sebastian "Sebbie" Silbermann committed
Feb 13, 2025 at 18:26 UTC
ed8b68dd178af17a2dd36c8678f81f8b454559a9
9 files changed
+96
-29
packages/react-dom/src/__tests__/ReactTestUtilsAct-test.js
-8
@@ -779,13 +779,5 @@ function runActTests(render, unmount, rerender) {
779
});
780
}
781
});
782
- describe('throw in prod mode', () => {
783
- // @gate !__DEV__
784
- it('warns if you try to use act() in prod mode', () => {
785
- expect(() => act(() => {})).toThrow(
786
- 'act(...) is not supported in production builds of React',
787
- );
788
- });
789
- });
782
});
783
}
packages/react-reconciler/src/__tests__/ReactIsomorphicAct-test.js
+12
@@ -50,6 +50,18 @@ describe('isomorphic act()', () => {
50
return text;
51
}
52
53
+ it('behavior in production', () => {
54
+ if (!__DEV__) {
55
+ if (gate('fb')) {
56
+ expect(() => act(() => {})).toThrow(
57
+ 'act(...) is not supported in production builds of React',
58
+ );
59
+ } else {
60
+ expect(React).not.toHaveProperty('act');
61
+ }
62
+ }
63
+ });
64
+
65
// @gate __DEV__
66
it('bypasses queueMicrotask', async () => {
67
const root = ReactNoop.createRoot();
packages/react-reconciler/src/__tests__/ReactOwnerStacks-test.js
+10
@@ -31,6 +31,16 @@ describe('ReactOwnerStacks', () => {
31
);
32
}
33
34
+ it('behavior in production', () => {
35
+ if (!__DEV__) {
36
+ if (gate('fb')) {
37
+ expect(React).toHaveProperty('captureOwnerStack', undefined);
38
+ } else {
39
+ expect(React).not.toHaveProperty('captureOwnerStack');
40
+ }
41
+ }
42
+ });
43
+
44
// @gate __DEV__ && enableOwnerStacks
45
it('can get the component owner stacks during rendering in dev', async () => {
46
let stack;
packages/react/index.js
-2
@@ -23,8 +23,6 @@ export type ElementRef<C> = React$ElementRef<C>;
23
export type Config<Props, DefaultProps> = React$Config<Props, DefaultProps>;
24
export type ChildrenArray<+T> = $ReadOnlyArray<ChildrenArray<T>> | T;
25
26
-// Export all exports so that they're available in tests.
27
-// We can't use export * from in Flow for some reason.
26
export {
27
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
28
__COMPILER_RUNTIME,
packages/react/index.stable.development.js
new
+51
@@ -0,0 +1,51 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+export {
11
+ __CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
12
+ __COMPILER_RUNTIME,
13
+ Children,
14
+ Component,
15
+ Fragment,
16
+ Profiler,
17
+ PureComponent,
18
+ StrictMode,
19
+ Suspense,
20
+ cloneElement,
21
+ createContext,
22
+ createElement,
23
+ createRef,
24
+ use,
25
+ forwardRef,
26
+ isValidElement,
27
+ lazy,
28
+ memo,
29
+ cache,
30
+ unstable_useCacheRefresh,
31
+ startTransition,
32
+ useId,
33
+ useCallback,
34
+ useContext,
35
+ useDebugValue,
36
+ useDeferredValue,
37
+ useEffect,
38
+ useImperativeHandle,
39
+ useInsertionEffect,
40
+ useLayoutEffect,
41
+ useMemo,
42
+ useReducer,
43
+ useOptimistic,
44
+ useRef,
45
+ useState,
46
+ useSyncExternalStore,
47
+ useTransition,
48
+ useActionState,
49
+ version,
50
+ act, // DEV-only
51
+} from './src/ReactClient';
packages/react/index.stable.js
-1
@@ -10,7 +10,6 @@
10
export {
11
__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,
12
__COMPILER_RUNTIME,
13
- act,
13
Children,
14
Component,
15
Fragment,
scripts/jest/TestFlags.js
+1
@@ -78,6 +78,7 @@ function getTestFlags() {
78
classic: releaseChannel === 'classic',
79
source: !process.env.IS_BUILD,
80
www,
81
+ fb: www || xplat,
82
83
// These aren't flags, just a useful aliases for tests.
84
enableActivity: releaseChannel === 'experimental' || www || xplat,
scripts/jest/setupHostConfigs.js
+13
-6
@@ -43,9 +43,9 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
43
}
44
45
resolvedEntry = nodePath.join(resolvedEntry, '..', entrypoint);
46
- const developmentEntry = resolvedEntry.replace('.js', '.development.js');
47
- if (fs.existsSync(developmentEntry)) {
48
- return developmentEntry;
46
+ const devEntry = resolvedEntry.replace('.js', '.development.js');
47
+ if (__DEV__ && fs.existsSync(devEntry)) {
48
+ return devEntry;
49
}
50
if (fs.existsSync(resolvedEntry)) {
51
return resolvedEntry;
@@ -60,13 +60,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
60
__EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js'
61
);
62
const devFBEntry = resolvedFBEntry.replace('.js', '.development.js');
63
- if (fs.existsSync(devFBEntry)) {
63
+ if (__DEV__ && fs.existsSync(devFBEntry)) {
64
return devFBEntry;
65
}
66
if (fs.existsSync(resolvedFBEntry)) {
67
return resolvedFBEntry;
68
}
69
const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js');
70
+ const devGenericFBEntry = resolvedGenericFBEntry.replace(
71
+ '.js',
72
+ '.development.js'
73
+ );
74
+ if (__DEV__ && fs.existsSync(devGenericFBEntry)) {
75
+ return devGenericFBEntry;
76
+ }
77
if (fs.existsSync(resolvedGenericFBEntry)) {
78
return resolvedGenericFBEntry;
79
}
@@ -77,14 +84,14 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
84
__EXPERIMENTAL__ ? '.experimental.js' : '.stable.js'
85
);
86
const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js');
80
- if (fs.existsSync(devForkedEntry)) {
87
+ if (__DEV__ && fs.existsSync(devForkedEntry)) {
88
return devForkedEntry;
89
}
90
if (fs.existsSync(resolvedForkedEntry)) {
91
return resolvedForkedEntry;
92
}
93
const plainDevEntry = resolvedEntry.replace('.js', '.development.js');
87
- if (fs.existsSync(plainDevEntry)) {
94
+ if (__DEV__ && fs.existsSync(plainDevEntry)) {
95
return plainDevEntry;
96
}
97
// Just use the plain .js one.
scripts/rollup/build.js
+9
-12
@@ -571,7 +571,7 @@ function shouldSkipBundle(bundle, bundleType) {
571
return false;
572
}
573
574
-function resolveEntryFork(resolvedEntry, isFBBundle) {
574
+function resolveEntryFork(resolvedEntry, isFBBundle, isDev) {
575
// Pick which entry point fork to use:
576
// .modern.fb.js
577
// .classic.fb.js
@@ -586,23 +586,20 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
586
'.js',
587
__EXPERIMENTAL__ ? '.modern.fb.js' : '.classic.fb.js'
588
);
589
- const developmentFBEntry = resolvedFBEntry.replace(
590
- '.js',
591
- '.development.js'
592
- );
593
- if (fs.existsSync(developmentFBEntry)) {
594
- return developmentFBEntry;
589
+ const devFBEntry = resolvedFBEntry.replace('.js', '.development.js');
590
+ if (isDev && fs.existsSync(devFBEntry)) {
591
+ return devFBEntry;
592
}
593
if (fs.existsSync(resolvedFBEntry)) {
594
return resolvedFBEntry;
595
}
596
const resolvedGenericFBEntry = resolvedEntry.replace('.js', '.fb.js');
600
- const developmentGenericFBEntry = resolvedGenericFBEntry.replace(
597
+ const devGenericFBEntry = resolvedGenericFBEntry.replace(
598
'.js',
599
'.development.js'
600
);
604
- if (fs.existsSync(developmentGenericFBEntry)) {
605
- return developmentGenericFBEntry;
601
+ if (isDev && fs.existsSync(devGenericFBEntry)) {
602
+ return devGenericFBEntry;
603
}
604
if (fs.existsSync(resolvedGenericFBEntry)) {
605
return resolvedGenericFBEntry;
@@ -614,7 +611,7 @@ function resolveEntryFork(resolvedEntry, isFBBundle) {
611
__EXPERIMENTAL__ ? '.experimental.js' : '.stable.js'
612
);
613
const devForkedEntry = resolvedForkedEntry.replace('.js', '.development.js');
617
- if (fs.existsSync(devForkedEntry)) {
614
+ if (isDev && fs.existsSync(devForkedEntry)) {
615
return devForkedEntry;
616
}
617
if (fs.existsSync(resolvedForkedEntry)) {
@@ -633,7 +630,7 @@ async function createBundle(bundle, bundleType) {
630
631
const {isFBWWWBundle, isFBRNBundle} = getBundleTypeFlags(bundleType);
632
636
- let resolvedEntry = resolveEntryFork(
633
+ const resolvedEntry = resolveEntryFork(
634
require.resolve(bundle.entry),
635
isFBWWWBundle || isFBRNBundle,
636
!isProductionBundleType(bundleType)