Test the node-register hooks in unit tests (#25132)

This commit is contained in:
Sebastian Markbåge
2022-08-24 19:05:39 -04:00
committed by GitHub
parent 3f70e68cea
commit 38c5d8a035
7 changed files with 94 additions and 62 deletions

View File

@@ -7,4 +7,4 @@
* @flow
*/
export * from './src/ReactFlightWebpackNodeRegister';
module.exports = require('./src/ReactFlightWebpackNodeRegister');

View File

@@ -35,6 +35,7 @@
"./writer.browser.server": "./writer.browser.server.js",
"./node-loader": "./esm/react-server-dom-webpack-node-loader.js",
"./node-register": "./node-register.js",
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"main": "index.js",

View File

@@ -57,7 +57,7 @@ module.exports = function register() {
},
};
(require: any).extensions['.client.js'] = function(module, path) {
Module._extensions['.client.js'] = function(module, path) {
const moduleId = url.pathToFileURL(path).href;
const moduleReference: {[string]: any} = {
$$typeof: MODULE_REFERENCE,

View File

@@ -17,14 +17,9 @@ global.TextDecoder = require('util').TextDecoder;
// TODO: we can replace this with FlightServer.act().
global.setImmediate = cb => cb();
let webpackModuleIdx = 0;
let webpackModules = {};
let webpackMap = {};
global.__webpack_require__ = function(id) {
return webpackModules[id];
};
let act;
let clientExports;
let webpackMap;
let Stream;
let React;
let ReactDOMClient;
@@ -35,15 +30,17 @@ let Suspense;
describe('ReactFlightDOM', () => {
beforeEach(() => {
jest.resetModules();
webpackModules = {};
webpackMap = {};
act = require('jest-react').act;
const WebpackMock = require('./utils/WebpackMock');
clientExports = WebpackMock.clientExports;
webpackMap = WebpackMock.webpackMap;
Stream = require('stream');
React = require('react');
Suspense = React.Suspense;
ReactDOMClient = require('react-dom/client');
ReactServerDOMWriter = require('react-server-dom-webpack/writer.node.server');
ReactServerDOMReader = require('react-server-dom-webpack');
Suspense = React.Suspense;
});
function getTestStream() {
@@ -64,22 +61,6 @@ describe('ReactFlightDOM', () => {
};
}
function moduleReference(moduleExport) {
const idx = webpackModuleIdx++;
webpackModules[idx] = {
d: moduleExport,
};
webpackMap['path/' + idx] = {
default: {
id: '' + idx,
chunks: [],
name: 'd',
},
};
const MODULE_TAG = Symbol.for('react.module.reference');
return {$$typeof: MODULE_TAG, filepath: 'path/' + idx, name: 'default'};
}
async function waitForSuspense(fn) {
while (true) {
try {
@@ -345,7 +326,7 @@ describe('ReactFlightDOM', () => {
return <div>{games}</div>;
}
const MyErrorBoundaryClient = moduleReference(MyErrorBoundary);
const MyErrorBoundaryClient = clientExports(MyErrorBoundary);
function ProfileContent() {
return (
@@ -470,7 +451,7 @@ describe('ReactFlightDOM', () => {
return <input />;
}
const InputClient = moduleReference(Input);
const InputClient = clientExports(Input);
// Server

View File

@@ -14,13 +14,9 @@ global.ReadableStream = require('web-streams-polyfill/ponyfill/es6').ReadableStr
global.TextEncoder = require('util').TextEncoder;
global.TextDecoder = require('util').TextDecoder;
let webpackModuleIdx = 0;
let webpackModules = {};
let webpackMap = {};
global.__webpack_require__ = function(id) {
return webpackModules[id];
};
let clientExports;
let webpackMap;
let webpackModules;
let act;
let React;
let ReactDOMClient;
@@ -32,9 +28,11 @@ let Suspense;
describe('ReactFlightDOMBrowser', () => {
beforeEach(() => {
jest.resetModules();
webpackModules = {};
webpackMap = {};
act = require('jest-react').act;
const WebpackMock = require('./utils/WebpackMock');
clientExports = WebpackMock.clientExports;
webpackMap = WebpackMock.webpackMap;
webpackModules = WebpackMock.webpackModules;
React = require('react');
ReactDOMClient = require('react-dom/client');
ReactDOMServer = require('react-dom/server.browser');
@@ -43,22 +41,6 @@ describe('ReactFlightDOMBrowser', () => {
Suspense = React.Suspense;
});
function moduleReference(moduleExport) {
const idx = webpackModuleIdx++;
webpackModules[idx] = {
d: moduleExport,
};
webpackMap['path/' + idx] = {
default: {
id: '' + idx,
chunks: [],
name: 'd',
},
};
const MODULE_TAG = Symbol.for('react.module.reference');
return {$$typeof: MODULE_TAG, filepath: 'path/' + idx, name: 'default'};
}
async function waitForSuspense(fn) {
while (true) {
try {
@@ -249,7 +231,7 @@ describe('ReactFlightDOMBrowser', () => {
return <div>{games}</div>;
}
const MyErrorBoundaryClient = moduleReference(MyErrorBoundary);
const MyErrorBoundaryClient = clientExports(MyErrorBoundary);
function ProfileContent() {
return (
@@ -478,19 +460,19 @@ describe('ReactFlightDOMBrowser', () => {
}
// The Client build may not have the same IDs as the Server bundles for the same
// component.
const ClientComponentOnTheClient = moduleReference(ClientComponent);
const ClientComponentOnTheServer = moduleReference(ClientComponent);
const ClientComponentOnTheClient = clientExports(ClientComponent);
const ClientComponentOnTheServer = clientExports(ClientComponent);
// In the SSR bundle this module won't exist. We simulate this by deleting it.
const clientId = webpackMap[ClientComponentOnTheClient.filepath].default.id;
const clientId = webpackMap[ClientComponentOnTheClient.filepath]['*'].id;
delete webpackModules[clientId];
// Instead, we have to provide a translation from the client meta data to the SSR
// meta data.
const ssrMetaData = webpackMap[ClientComponentOnTheServer.filepath].default;
const ssrMetaData = webpackMap[ClientComponentOnTheServer.filepath]['*'];
const translationMap = {
[clientId]: {
d: ssrMetaData,
'*': ssrMetaData,
},
};

View File

@@ -0,0 +1,67 @@
/**
* 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.
*/
'use strict';
const url = require('url');
const Module = require('module');
let webpackModuleIdx = 0;
const webpackModules = {};
const webpackMap = {};
global.__webpack_require__ = function(id) {
return webpackModules[id];
};
const previousLoader = Module._extensions['.client.js'];
const register = require('react-server-dom-webpack/node-register');
// Register node loader
register();
const nodeLoader = Module._extensions['.client.js'];
if (previousLoader === nodeLoader) {
throw new Error(
'Expected the Node loader to register the .client.js extension',
);
}
Module._extensions['.client.js'] = previousLoader;
exports.webpackMap = webpackMap;
exports.webpackModules = webpackModules;
exports.clientExports = function clientExports(moduleExports) {
const idx = '' + webpackModuleIdx++;
webpackModules[idx] = moduleExports;
const path = url.pathToFileURL(idx).href;
webpackMap[path] = {
'': {
id: idx,
chunks: [],
name: '',
},
'*': {
id: idx,
chunks: [],
name: '*',
},
};
for (const name in moduleExports) {
webpackMap[path] = {
[name]: {
id: idx,
chunks: [],
name: name,
},
};
}
const mod = {exports: {}};
nodeLoader(mod, idx);
return mod.exports;
};

View File

@@ -408,7 +408,8 @@ const bundles = [
{
bundleTypes: [NODE_ES2015],
moduleType: RENDERER_UTILS,
entry: 'react-server-dom-webpack/node-register',
entry: 'react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js',
name: 'react-server-dom-webpack-node-register',
global: 'ReactFlightWebpackNodeRegister',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,