@samitouri / QOS-React-2 / commits / 2d2f2af29b

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',