Skip to content

Commit

Permalink
Server render fork for react-dom (#25436)
Browse files Browse the repository at this point in the history
Publish an aliasable entry for `react-dom` top level package exports for use in server environments. This is a stub containing only the exports that we expect to retain in the top level once 19 is released
  • Loading branch information
gnoff committed Oct 10, 2022
1 parent 513417d commit aa9988e
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 2 deletions.
7 changes: 7 additions & 0 deletions packages/react-dom/npm/server-rendering-stub.js
Original file line number Diff line number Diff line change
@@ -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');
}
2 changes: 2 additions & 0 deletions packages/react-dom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"static.js",
"static.browser.js",
"static.node.js",
"server-rendering-stub.js",
"test-utils.js",
"unstable_testing.js",
"cjs/",
Expand All @@ -59,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",
Expand Down
22 changes: 22 additions & 0 deletions packages/react-dom/server-rendering-stub.experimental.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* 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.

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';
21 changes: 21 additions & 0 deletions packages/react-dom/server-rendering-stub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* 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.

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';
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* 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', () => require('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 <div>foo</div>;
}
const html = ReactDOMFizzServer.renderToString(<App />);
expect(html).toEqual(
'<link href="foo" rel="preload" as="style"/><link rel="stylesheet" href="bar" data-rprec="default"/><div>foo</div>',
);
});

it('provides a stub for createPortal', async () => {
expect(() => {
ReactDOM.createPortal();
}).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.',
);
});

it('provides a stub for flushSync', async () => {
let x = false;
expect(() => {
ReactDOM.flushSync(() => (x = 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);
});
});
25 changes: 25 additions & 0 deletions packages/react-dom/src/server/ReactDOMServerRenderingStub.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* 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() {
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() {
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.',
);
}
4 changes: 3 additions & 1 deletion scripts/error-codes/codes.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
12 changes: 12 additions & 0 deletions scripts/rollup/bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
2 changes: 1 addition & 1 deletion scripts/rollup/forks.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
2 changes: 2 additions & 0 deletions scripts/shared/inlinedHostConfigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
],
Expand Down Expand Up @@ -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',
],
Expand Down

0 comments on commit aa9988e

Please sign in to comment.