Restrict React DOM imports from Server Components (#27382)
Adds a separate entry point for the react-dom package when it's accessed from a Server Component environment, using the "react-server" export condition. When you're inside a Server Component module, you won't be able to import client-only APIs like useState. This applies to almost all React DOM exports, except for Float ones like preload.
Andrew Clark committed
Sep 15, 2023 at 14:53 UTC
2d2f2af29bc3175530a158bc90189fc6f12f7855
6 files changed
+56
-2
packages/react-dom/npm/react-dom.shared-subset.js
new
+7
@@ -0,0 +1,7 @@
1
+'use strict';
2
+
3
+if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./cjs/react-dom.shared-subset.production.min.js');
5
+} else {
6
+ module.exports = require('./cjs/react-dom.shared-subset.development.js');
7
+}
packages/react-dom/package.json
+4
-1
@@ -46,7 +46,10 @@
46
"umd/"
47
],
48
"exports": {
49
- ".": "./index.js",
49
+ ".": {
50
+ "react-server": "./react-dom.shared-subset.js",
51
+ "default": "./index.js"
52
+ },
53
"./client": "./client.js",
54
"./server": {
55
"workerd": "./server.edge.js",
packages/react-dom/src/ReactDOMSharedSubset.js
new
+19
@@ -0,0 +1,19 @@
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
+// This is the subset of APIs that can be accessed from Server Component modules
11
+export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './ReactDOMSharedInternals';
12
+export {
13
+ prefetchDNS,
14
+ preconnect,
15
+ preload,
16
+ preloadModule,
17
+ preinit,
18
+ preinitModule,
19
+} from './shared/ReactDOMFloat';
scripts/rollup/bundles.js
+12
@@ -165,6 +165,18 @@ const bundles = [
165
externals: ['react'],
166
},
167
168
+ /******* React DOM Shared Subset *******/
169
+ {
170
+ bundleTypes: [NODE_DEV, NODE_PROD],
171
+ moduleType: RENDERER,
172
+ entry: 'react-dom/src/ReactDOMSharedSubset.js',
173
+ name: 'react-dom.shared-subset',
174
+ global: 'ReactDOM',
175
+ minifyWithProdErrorCodes: false,
176
+ wrapWithModuleBoundaries: false,
177
+ externals: ['react'],
178
+ },
179
+
180
/******* Test Utils *******/
181
{
182
moduleType: RENDERER_UTILS,
scripts/rollup/forks.js
+5
-1
@@ -84,7 +84,11 @@ const forks = Object.freeze({
84
entry,
85
dependencies
86
) => {
87
- if (entry === 'react-dom' || entry === 'react-dom/server-rendering-stub') {
87
+ if (
88
+ entry === 'react-dom' ||
89
+ entry === 'react-dom/server-rendering-stub' ||
90
+ entry === 'react-dom/src/ReactDOMSharedSubset.js'
91
+ ) {
92
return './packages/react-dom/src/ReactDOMSharedInternals.js';
93
}
94
if (
scripts/shared/inlinedHostConfigs.js
+9
@@ -11,6 +11,7 @@ module.exports = [
11
shortName: 'dom-node',
12
entryPoints: [
13
'react-dom',
14
+ 'react-dom/src/ReactDOMSharedSubset.js',
15
'react-dom/unstable_testing',
16
'react-dom/src/server/react-dom-server.node.js',
17
'react-dom/static.node',
@@ -21,6 +22,7 @@ module.exports = [
22
],
23
paths: [
24
'react-dom',
25
+ 'react-dom/src/ReactDOMSharedSubset.js',
26
'react-dom-bindings',
27
'react-dom/client',
28
'react-dom/server',
@@ -73,6 +75,7 @@ module.exports = [
75
],
76
paths: [
77
'react-dom',
78
+ 'react-dom/src/ReactDOMSharedSubset.js',
79
'react-dom-bindings',
80
'react-dom/client',
81
'react-dom/server.browser',
@@ -101,6 +104,7 @@ module.exports = [
104
entryPoints: ['react-server-dom-esm/client.browser'],
105
paths: [
106
'react-dom',
107
+ 'react-dom/src/ReactDOMSharedSubset.js',
108
'react-dom/client',
109
'react-dom/server',
110
'react-dom/server.node',
@@ -128,6 +132,7 @@ module.exports = [
132
],
133
paths: [
134
'react-dom',
135
+ 'react-dom/src/ReactDOMSharedSubset.js',
136
'react-dom-bindings',
137
'react-dom/client',
138
'react-dom/server.edge',
@@ -158,6 +163,7 @@ module.exports = [
163
],
164
paths: [
165
'react-dom',
166
+ 'react-dom/src/ReactDOMSharedSubset.js',
167
'react-dom-bindings',
168
'react-dom/client',
169
'react-dom/server',
@@ -192,6 +198,7 @@ module.exports = [
198
],
199
paths: [
200
'react-dom',
201
+ 'react-dom/src/ReactDOMSharedSubset.js',
202
'react-dom-bindings',
203
'react-dom/client',
204
'react-dom/server',
@@ -224,6 +231,7 @@ module.exports = [
231
],
232
paths: [
233
'react-dom',
234
+ 'react-dom/src/ReactDOMSharedSubset.js',
235
'react-dom-bindings',
236
'react-server-dom-webpack',
237
'react-dom/src/server/ReactDOMLegacyServerImpl.js', // not an entrypoint, but only usable in *Brower and *Node files
@@ -241,6 +249,7 @@ module.exports = [
249
entryPoints: ['react-server-dom-fb/src/ReactDOMServerFB.js'],
250
paths: [
251
'react-dom',
252
+ 'react-dom/src/ReactDOMSharedSubset.js',
253
'react-dom-bindings',
254
'react-server-dom-fb',
255
'shared/ReactDOMSharedInternals',