From 779b8c07f55d190fa1cc2db956299ebdfd5d2ba8 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Wed, 5 Oct 2022 14:39:33 -0700 Subject: [PATCH 1/6] initial pass at a server render fork for react-dom --- packages/react-dom/npm/server-rendering-stub.js | 7 +++++++ packages/react-dom/package.json | 1 + .../react-dom/server-rendering-stub.experimental.js | 13 +++++++++++++ packages/react-dom/server-rendering-stub.js | 12 ++++++++++++ scripts/rollup/bundles.js | 12 ++++++++++++ scripts/rollup/forks.js | 2 +- scripts/shared/inlinedHostConfigs.js | 2 ++ 7 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 packages/react-dom/npm/server-rendering-stub.js create mode 100644 packages/react-dom/server-rendering-stub.experimental.js create mode 100644 packages/react-dom/server-rendering-stub.js diff --git a/packages/react-dom/npm/server-rendering-stub.js b/packages/react-dom/npm/server-rendering-stub.js new file mode 100644 index 0000000000000..fa2464a3e59fe --- /dev/null +++ b/packages/react-dom/npm/server-rendering-stub.js @@ -0,0 +1,7 @@ +'use strict'; + +if (process.env.NODE_ENV === 'production') { + module.exports = require('./cjs/react-dom-server-rendering-stub.production.min.js'); +} else { + module.exports = require('./cjs/react-dom-server-rendering-stub.development.js'); +} diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index ca84673501ffd..1027c7c4e8536 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -49,6 +49,7 @@ "browser": "./server.browser.js", "default": "./server.node.js" }, + "./server-rendering-stub": "./server-rendering-stub.js", "./server.browser": "./server.browser.js", "./server.node": "./server.node.js", "./static": { diff --git a/packages/react-dom/server-rendering-stub.experimental.js b/packages/react-dom/server-rendering-stub.experimental.js new file mode 100644 index 0000000000000..09d002f3ee880 --- /dev/null +++ b/packages/react-dom/server-rendering-stub.experimental.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its 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 all exports so that they're available in tests. +// We can't use export * from in Flow for some reason. +export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/ReactDOMSharedInternals'; +export {preinit, preload} from 'react-dom-bindings/src/shared/ReactDOMFloat'; diff --git a/packages/react-dom/server-rendering-stub.js b/packages/react-dom/server-rendering-stub.js new file mode 100644 index 0000000000000..b0703786b963c --- /dev/null +++ b/packages/react-dom/server-rendering-stub.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its 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 all exports so that they're available in tests. +// We can't use export * from in Flow for some reason. +export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/ReactDOMSharedInternals'; diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js index df67873c8c485..1f7297a8f2174 100644 --- a/scripts/rollup/bundles.js +++ b/scripts/rollup/bundles.js @@ -351,6 +351,18 @@ const bundles = [ externals: ['react', 'util', 'stream', 'react-dom'], }, + /******* React DOM Server Render Stub *******/ + { + bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD], + moduleType: RENDERER, + entry: 'react-dom/server-rendering-stub', + name: 'react-dom-server-rendering-stub', + global: 'ReactDOMServerRenderingStub', + minifyWithProdErrorCodes: true, + wrapWithModuleBoundaries: false, + externals: ['react'], + }, + /******* React Server DOM Webpack Writer *******/ { bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD], diff --git a/scripts/rollup/forks.js b/scripts/rollup/forks.js index b745918acfa3d..15856230bacc7 100644 --- a/scripts/rollup/forks.js +++ b/scripts/rollup/forks.js @@ -74,7 +74,7 @@ const forks = Object.freeze({ entry, dependencies ) => { - if (entry === 'react-dom') { + if (entry === 'react-dom' || entry === 'react-dom/server-rendering-stub') { return './packages/react-dom/src/ReactDOMSharedInternals.js'; } if ( diff --git a/scripts/shared/inlinedHostConfigs.js b/scripts/shared/inlinedHostConfigs.js index 2cd0f8400341e..1113bad1247d0 100644 --- a/scripts/shared/inlinedHostConfigs.js +++ b/scripts/shared/inlinedHostConfigs.js @@ -14,6 +14,7 @@ module.exports = [ 'react-dom/unstable_testing', 'react-dom/src/server/ReactDOMFizzServerNode.js', 'react-dom/static.node', + 'react-dom/server-rendering-stub', 'react-server-dom-webpack/writer.node.server', 'react-server-dom-webpack', ], @@ -49,6 +50,7 @@ module.exports = [ 'react-dom/unstable_testing', 'react-dom/src/server/ReactDOMFizzServerBrowser.js', 'react-dom/static.browser', + 'react-dom/server-rendering-stub', 'react-server-dom-webpack/writer.browser.server', 'react-server-dom-webpack', ], From 78f496fb7569cc1725463602cf1ca23a9e5e85f1 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Thu, 6 Oct 2022 09:19:00 -0700 Subject: [PATCH 2/6] add stubs --- packages/react-dom/package.json | 3 +- .../server-rendering-stub.experimental.js | 9 +++ packages/react-dom/server-rendering-stub.js | 9 +++ .../react-dom-server-rendering-stub-test.js | 79 +++++++++++++++++++ .../src/server/ReactDOMServerRenderingStub.js | 29 +++++++ 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js create mode 100644 packages/react-dom/src/server/ReactDOMServerRenderingStub.js diff --git a/packages/react-dom/package.json b/packages/react-dom/package.json index 1027c7c4e8536..c39432aa32046 100644 --- a/packages/react-dom/package.json +++ b/packages/react-dom/package.json @@ -35,6 +35,7 @@ "static.js", "static.browser.js", "static.node.js", + "server-rendering-stub.js", "test-utils.js", "unstable_testing.js", "cjs/", @@ -49,7 +50,6 @@ "browser": "./server.browser.js", "default": "./server.node.js" }, - "./server-rendering-stub": "./server-rendering-stub.js", "./server.browser": "./server.browser.js", "./server.node": "./server.node.js", "./static": { @@ -60,6 +60,7 @@ }, "./static.browser": "./static.browser.js", "./static.node": "./static.node.js", + "./server-rendering-stub": "./server-rendering-stub.js", "./profiling": "./profiling.js", "./test-utils": "./test-utils.js", "./unstable_testing": "./unstable_testing.js", diff --git a/packages/react-dom/server-rendering-stub.experimental.js b/packages/react-dom/server-rendering-stub.experimental.js index 09d002f3ee880..d87679dac0c06 100644 --- a/packages/react-dom/server-rendering-stub.experimental.js +++ b/packages/react-dom/server-rendering-stub.experimental.js @@ -9,5 +9,14 @@ // Export all exports so that they're available in tests. // We can't use export * from in Flow for some reason. + +import ReactVersion from 'shared/ReactVersion'; +export {ReactVersion as version}; + export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/ReactDOMSharedInternals'; + +export { + createPortal, + flushSync, +} from './src/server/ReactDOMServerRenderingStub'; export {preinit, preload} from 'react-dom-bindings/src/shared/ReactDOMFloat'; diff --git a/packages/react-dom/server-rendering-stub.js b/packages/react-dom/server-rendering-stub.js index b0703786b963c..7b673e903c987 100644 --- a/packages/react-dom/server-rendering-stub.js +++ b/packages/react-dom/server-rendering-stub.js @@ -9,4 +9,13 @@ // Export all exports so that they're available in tests. // We can't use export * from in Flow for some reason. + +import ReactVersion from 'shared/ReactVersion'; +export {ReactVersion as version}; + export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './src/ReactDOMSharedInternals'; + +export { + createPortal, + flushSync, +} from './src/server/ReactDOMServerRenderingStub'; diff --git a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js new file mode 100644 index 0000000000000..da907a69f306f --- /dev/null +++ b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js @@ -0,0 +1,79 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @emails react-core + */ + +'use strict'; + +let React; +let ReactDOM; +let ReactDOMFizzServer; + +describe('react-dom-server-rendering-stub', () => { + beforeEach(() => { + jest.mock('react-dom', () => { + return __EXPERIMENTAL__ + ? jest.requireActual('react-dom/server-rendering-stub.experimental') + : jest.requireActual('react-dom/server-rendering-stub'); + }); + + React = require('react'); + ReactDOM = require('react-dom'); + ReactDOMFizzServer = require('react-dom/server'); + }); + + it('exports a version', () => { + expect(ReactDOM.version).toBeTruthy(); + }); + + it('exports that are expected to be client only in the future are not exported', () => { + expect(ReactDOM.createRoot).toBe(undefined); + expect(ReactDOM.hydrateRoot).toBe(undefined); + expect(ReactDOM.findDOMNode).toBe(undefined); + expect(ReactDOM.hydrate).toBe(undefined); + expect(ReactDOM.render).toBe(undefined); + expect(ReactDOM.unmountComponentAtNode).toBe(undefined); + expect(ReactDOM.unstable_batchedUpdates).toBe(undefined); + expect(ReactDOM.unstable_createEventHandle).toBe(undefined); + expect(ReactDOM.unstable_flushControlled).toBe(undefined); + expect(ReactDOM.unstable_isNewReconciler).toBe(undefined); + expect(ReactDOM.unstable_renderSubtreeIntoContainer).toBe(undefined); + expect(ReactDOM.unstable_runWithPriority).toBe(undefined); + }); + + // @gate enableFloat + it('provides preload and preinit exports', async () => { + function App() { + ReactDOM.preload('foo', {as: 'style'}); + ReactDOM.preinit('bar', {as: 'style'}); + return
foo
; + } + let html = ReactDOMFizzServer.renderToString(); + expect(html).toEqual( + '
foo
', + ); + }); + + it('provides a stub for createPortal', async () => { + expect(() => { + ReactDOM.createPortal(); + }).toErrorDev( + 'Warning: createPortal was called on the server. Portals are not currently supported on the server. Update your program to conditionally call createPortal on the client only.', + {withoutStack: true}, + ); + }); + + it('provides a stub for flushSync', async () => { + let x = false; + expect(() => { + ReactDOM.flushSync(() => (x = true)); + }).toErrorDev( + 'Warning: flushSync was called on the server. This is likely caused by a function being called during render or in module scope that was supposed to be called from an effect or event handler. On the server, flushSync does nothing.', + {withoutStack: true}, + ); + }); +}); diff --git a/packages/react-dom/src/server/ReactDOMServerRenderingStub.js b/packages/react-dom/src/server/ReactDOMServerRenderingStub.js new file mode 100644 index 0000000000000..644eb6250c414 --- /dev/null +++ b/packages/react-dom/src/server/ReactDOMServerRenderingStub.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) Facebook, Inc. and its 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 function createPortal() { + if (__DEV__) { + console.error( + 'createPortal was called on the server. Portals are not currently' + + ' supported on the server. Update your program to conditionally call' + + ' createPortal on the client only.', + ); + } +} + +export function flushSync() { + if (__DEV__) { + console.error( + 'flushSync was called on the server. This is likely caused by a' + + ' function being called during render or in module scope that was' + + ' supposed to be called from an effect or event handler. On the' + + ' server, flushSync does nothing.', + ); + } +} From d8c25f1f3d3afd71a4d353bd252ec31fc1be1d3e Mon Sep 17 00:00:00 2001 From: Josh Story Date: Thu, 6 Oct 2022 09:40:28 -0700 Subject: [PATCH 3/6] lints and tests --- .../src/__tests__/react-dom-server-rendering-stub-test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js index da907a69f306f..e72814d0ed66c 100644 --- a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js +++ b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js @@ -52,7 +52,7 @@ describe('react-dom-server-rendering-stub', () => { ReactDOM.preinit('bar', {as: 'style'}); return
foo
; } - let html = ReactDOMFizzServer.renderToString(); + const html = ReactDOMFizzServer.renderToString(); expect(html).toEqual( '
foo
', ); @@ -75,5 +75,6 @@ describe('react-dom-server-rendering-stub', () => { 'Warning: flushSync was called on the server. This is likely caused by a function being called during render or in module scope that was supposed to be called from an effect or event handler. On the server, flushSync does nothing.', {withoutStack: true}, ); + expect(x).toBe(false); }); }); From 540f419b066f7f24af396575973bad6845a099cf Mon Sep 17 00:00:00 2001 From: Josh Story Date: Thu, 6 Oct 2022 10:15:28 -0700 Subject: [PATCH 4/6] try using require --- .../src/__tests__/react-dom-server-rendering-stub-test.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js index e72814d0ed66c..243721f7cf638 100644 --- a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js +++ b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js @@ -15,11 +15,7 @@ let ReactDOMFizzServer; describe('react-dom-server-rendering-stub', () => { beforeEach(() => { - jest.mock('react-dom', () => { - return __EXPERIMENTAL__ - ? jest.requireActual('react-dom/server-rendering-stub.experimental') - : jest.requireActual('react-dom/server-rendering-stub'); - }); + jest.mock('react-dom', () => require('react-dom/server-rendering-stub')); React = require('react'); ReactDOM = require('react-dom'); From b7d0ac49d1f08b7d9ee30fc83853bdadcea3d92b Mon Sep 17 00:00:00 2001 From: Josh Story Date: Thu, 6 Oct 2022 23:44:27 -0700 Subject: [PATCH 5/6] throw errors rather than warn in dev --- .../react-dom-server-rendering-stub-test.js | 10 +++---- .../src/server/ReactDOMServerRenderingStub.js | 26 ++++++++----------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js index 243721f7cf638..1cf80ebf1dec4 100644 --- a/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js +++ b/packages/react-dom/src/__tests__/react-dom-server-rendering-stub-test.js @@ -57,9 +57,8 @@ describe('react-dom-server-rendering-stub', () => { it('provides a stub for createPortal', async () => { expect(() => { ReactDOM.createPortal(); - }).toErrorDev( - 'Warning: createPortal was called on the server. Portals are not currently supported on the server. Update your program to conditionally call createPortal on the client only.', - {withoutStack: true}, + }).toThrow( + 'createPortal was called on the server. Portals are not currently supported on the server. Update your program to conditionally call createPortal on the client only.', ); }); @@ -67,9 +66,8 @@ describe('react-dom-server-rendering-stub', () => { let x = false; expect(() => { ReactDOM.flushSync(() => (x = true)); - }).toErrorDev( - 'Warning: flushSync was called on the server. This is likely caused by a function being called during render or in module scope that was supposed to be called from an effect or event handler. On the server, flushSync does nothing.', - {withoutStack: true}, + }).toThrow( + 'flushSync was called on the server. This is likely caused by a function being called during render or in module scope that was intended to be called from an effect or event handler. Update your to not call flushSync no the server.', ); expect(x).toBe(false); }); diff --git a/packages/react-dom/src/server/ReactDOMServerRenderingStub.js b/packages/react-dom/src/server/ReactDOMServerRenderingStub.js index 644eb6250c414..7d29f2aaabf16 100644 --- a/packages/react-dom/src/server/ReactDOMServerRenderingStub.js +++ b/packages/react-dom/src/server/ReactDOMServerRenderingStub.js @@ -8,22 +8,18 @@ */ export function createPortal() { - if (__DEV__) { - console.error( - 'createPortal was called on the server. Portals are not currently' + - ' supported on the server. Update your program to conditionally call' + - ' createPortal on the client only.', - ); - } + throw new Error( + 'createPortal was called on the server. Portals are not currently' + + ' supported on the server. Update your program to conditionally call' + + ' createPortal on the client only.', + ); } export function flushSync() { - if (__DEV__) { - console.error( - 'flushSync was called on the server. This is likely caused by a' + - ' function being called during render or in module scope that was' + - ' supposed to be called from an effect or event handler. On the' + - ' server, flushSync does nothing.', - ); - } + throw new Error( + 'flushSync was called on the server. This is likely caused by a' + + ' function being called during render or in module scope that was' + + ' intended to be called from an effect or event handler. Update your' + + ' to not call flushSync no the server.', + ); } From 7e8a0f40318ed49a09ffff5e2c5fd0f16238d4b6 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Thu, 6 Oct 2022 23:49:42 -0700 Subject: [PATCH 6/6] codes --- scripts/error-codes/codes.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/error-codes/codes.json b/scripts/error-codes/codes.json index 48fcf61717273..9995cc0456835 100644 --- a/scripts/error-codes/codes.json +++ b/scripts/error-codes/codes.json @@ -432,5 +432,7 @@ "444": "getResource encountered a resource type it did not expect: \"%s\". this is a bug in React.", "445": "\"currentResources\" was expected to exist. This is a bug in React.", "446": "\"resourceRoot\" was expected to exist. This is a bug in React.", - "447": "While attempting to insert a Resource, React expected the Document to contain a head element but it was not found." + "447": "While attempting to insert a Resource, React expected the Document to contain a head element but it was not found.", + "448": "createPortal was called on the server. Portals are not currently supported on the server. Update your program to conditionally call createPortal on the client only.", + "449": "flushSync was called on the server. This is likely caused by a function being called during render or in module scope that was intended to be called from an effect or event handler. Update your to not call flushSync no the server." }