@samitouri / QOS-React-2 / commits / 67a61d5bd7

[Flight Fixture] Show SSR Support with CSS (#26263)

Builds on #26257. To do this we need access to a manifest for which scripts and CSS are used for each "page" (entrypoint). The initial script to bootstrap the app is inserted with `bootstrapScripts`. Subsequent content are loaded using the chunks mechanism built-in. The stylesheets for each pages are prepended to each RSC payload and rendered using Float. This doesn't yet support styles imported in components that are also SSR:ed nor imported through Server Components. That's more complex and not implemented in the node loader. HMR doesn't work after reloads right now because the SSR renderer isn't hot reloaded because there's no idiomatic way to hot reload ESM modules in Node.js yet. Without killing the HMR server. This leads to hydration mismatches when reloading the page after a hot reload. Notably this doesn't show serializing the stream through the HTML like real implementations do. This will lead to possible hydration mismatches based on the data. However, manually serializing the stream as a string isn't exactly correct due to binary data. It's not the idiomatic way this is supposed to work. This will all be built-in which will make this automatic in the future.

Sebastian Markbåge committed Feb 28, 2023 at 19:44 UTC 67a61d5bd7f731f5eb37d148dfc4ae0515a8c934
15 files changed +312 -183
fixtures/flight/.gitignore
-1
@@ -10,7 +10,6 @@
10
11 # production
12 /build
13 -/dist
13 .eslintcache
14
15 # misc
fixtures/flight/config/paths.js
-1
@@ -56,7 +56,6 @@ module.exports = {
56 appPath: resolveApp('.'),
57 appBuild: resolveApp(buildPath),
58 appPublic: resolveApp('public'),
59 - appHtml: resolveApp('public/index.html'),
59 appIndexJs: resolveModule(resolveApp, 'src/index'),
60 appPackageJson: resolveApp('package.json'),
61 appSrc: resolveApp('src'),
fixtures/flight/config/webpack.config.js
+36 -49
@@ -9,13 +9,10 @@ const {createHash} = require('crypto');
9 const path = require('path');
10 const webpack = require('webpack');
11 const resolve = require('resolve');
12 -const HtmlWebpackPlugin = require('html-webpack-plugin');
12 const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
14 -const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
13 const TerserPlugin = require('terser-webpack-plugin');
14 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
15 const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
18 -const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
16 const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
17 const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
18 const ESLintPlugin = require('eslint-webpack-plugin');
@@ -28,6 +25,7 @@ const ForkTsCheckerWebpackPlugin =
25 ? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin')
26 : require('react-dev-utils/ForkTsCheckerWebpackPlugin');
27 const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
28 +const {WebpackManifestPlugin} = require('webpack-manifest-plugin');
29
30 function createEnvironmentHash(env) {
31 const hash = createHash('md5');
@@ -116,7 +114,7 @@ module.exports = function (webpackEnv) {
114 const getStyleLoaders = (cssOptions, preProcessor) => {
115 const loaders = [
116 isEnvDevelopment && require.resolve('style-loader'),
119 - isEnvProduction && {
117 + {
118 loader: MiniCssExtractPlugin.loader,
119 // css is located in `static/css`, use '../../' to locate index.html folder
120 // in production `paths.publicUrlOrPath` can be a relative path
@@ -578,44 +576,6 @@ module.exports = function (webpackEnv) {
576 },
577 plugins: [
578 new webpack.HotModuleReplacementPlugin(),
581 - // Generates an `index.html` file with the <script> injected.
582 - new HtmlWebpackPlugin(
583 - Object.assign(
584 - {},
585 - {
586 - inject: true,
587 - template: paths.appHtml,
588 - },
589 - isEnvProduction
590 - ? {
591 - minify: {
592 - removeComments: true,
593 - collapseWhitespace: true,
594 - removeRedundantAttributes: true,
595 - useShortDoctype: true,
596 - removeEmptyAttributes: true,
597 - removeStyleLinkTypeAttributes: true,
598 - keepClosingSlash: true,
599 - minifyJS: true,
600 - minifyCSS: true,
601 - minifyURLs: true,
602 - },
603 - }
604 - : undefined
605 - )
606 - ),
607 - // Inlines the webpack runtime script. This script is too small to warrant
608 - // a network request.
609 - // https://github.com/facebook/create-react-app/issues/5358
610 - isEnvProduction &&
611 - shouldInlineRuntimeChunk &&
612 - new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
613 - // Makes some environment variables available in index.html.
614 - // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
615 - // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
616 - // It will be an empty string unless you specify "homepage"
617 - // in `package.json`, in which case it will be the pathname of that URL.
618 - new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
579 // This gives some necessary context to module not found errors, such as
580 // the requesting resource.
581 new ModuleNotFoundPlugin(paths.appPath),
@@ -636,13 +596,40 @@ module.exports = function (webpackEnv) {
596 // a plugin that prints an error when you attempt to do this.
597 // See https://github.com/facebook/create-react-app/issues/240
598 isEnvDevelopment && new CaseSensitivePathsPlugin(),
639 - isEnvProduction &&
640 - new MiniCssExtractPlugin({
641 - // Options similar to the same options in webpackOptions.output
642 - // both options are optional
643 - filename: 'static/css/[name].[contenthash:8].css',
644 - chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
645 - }),
599 + new MiniCssExtractPlugin({
600 + // Options similar to the same options in webpackOptions.output
601 + // both options are optional
602 + filename: 'static/css/[name].[contenthash:8].css',
603 + chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
604 + }),
605 + // Generate a manifest containing the required script / css for each entry.
606 + new WebpackManifestPlugin({
607 + fileName: 'entrypoint-manifest.json',
608 + publicPath: paths.publicUrlOrPath,
609 + generate: (seed, files, entrypoints) => {
610 + const entrypointFiles = entrypoints.main.filter(
611 + fileName => !fileName.endsWith('.map')
612 + );
613 +
614 + const processedEntrypoints = {};
615 + for (let key in entrypoints) {
616 + processedEntrypoints[key] = {
617 + js: entrypoints[key].filter(
618 + filename =>
619 + // Include JS assets but ignore hot updates because they're not
620 + // safe to include as async script tags.
621 + filename.endsWith('.js') &&
622 + !filename.endsWith('.hot-update.js')
623 + ),
624 + css: entrypoints[key].filter(filename =>
625 + filename.endsWith('.css')
626 + ),
627 + };
628 + }
629 +
630 + return processedEntrypoints;
631 + },
632 + }),
633 // Moment.js is an extremely popular library that bundles large locale files
634 // by default due to how webpack interprets its code. This is a practical
635 // solution that requires the user to opt into importing specific locales.
fixtures/flight/loader/global.js new
+51
@@ -0,0 +1,51 @@
1 +import babel from '@babel/core';
2 +
3 +const babelOptions = {
4 + babelrc: false,
5 + ignore: [/\/(build|node_modules)\//],
6 + plugins: [
7 + '@babel/plugin-syntax-import-meta',
8 + '@babel/plugin-transform-react-jsx',
9 + ],
10 +};
11 +
12 +export async function load(url, context, defaultLoad) {
13 + const {format} = context;
14 + const result = await defaultLoad(url, context, defaultLoad);
15 + if (result.format === 'module') {
16 + const opt = Object.assign({filename: url}, babelOptions);
17 + const newResult = await babel.transformAsync(result.source, opt);
18 + if (!newResult) {
19 + if (typeof result.source === 'string') {
20 + return result;
21 + }
22 + return {
23 + source: Buffer.from(result.source).toString('utf8'),
24 + format: 'module',
25 + };
26 + }
27 + return {source: newResult.code, format: 'module'};
28 + }
29 + return defaultLoad(url, context, defaultLoad);
30 +}
31 +
32 +async function babelTransformSource(source, context, defaultTransformSource) {
33 + const {format} = context;
34 + if (format === 'module') {
35 + const opt = Object.assign({filename: context.url}, babelOptions);
36 + const newResult = await babel.transformAsync(source, opt);
37 + if (!newResult) {
38 + if (typeof source === 'string') {
39 + return {source};
40 + }
41 + return {
42 + source: Buffer.from(source).toString('utf8'),
43 + };
44 + }
45 + return {source: newResult.code};
46 + }
47 + return defaultTransformSource(source, context, defaultTransformSource);
48 +}
49 +
50 +export const transformSource =
51 + process.version < 'v16' ? babelTransformSource : undefined;
fixtures/flight/loader/region.js renamed
fixtures/flight/package.json
+8 -7
@@ -43,10 +43,10 @@
43 "postcss-normalize": "^10.0.1",
44 "postcss-preset-env": "^7.0.1",
45 "prompts": "^2.4.2",
46 - "react": "^18.2.0",
46 + "react": "experimental",
47 "react-app-polyfill": "^3.0.0",
48 "react-dev-utils": "^12.0.1",
49 - "react-dom": "^18.2.0",
49 + "react-dom": "experimental",
50 "react-refresh": "^0.11.0",
51 "resolve": "^1.20.0",
52 "resolve-url-loader": "^4.0.0",
@@ -59,17 +59,18 @@
59 "undici": "^5.20.0",
60 "webpack": "^5.64.4",
61 "webpack-dev-middleware": "^5.3.1",
62 - "webpack-hot-middleware": "^2.25.3"
62 + "webpack-hot-middleware": "^2.25.3",
63 + "webpack-manifest-plugin": "^4.0.2"
64 },
65 "scripts": {
66 "predev": "cp -r ../../build/oss-experimental/* ./node_modules/",
67 "prebuild": "cp -r ../../build/oss-experimental/* ./node_modules/",
68 "dev": "concurrently \"npm run dev:region\" \"npm run dev:global\"",
68 - "dev:global": "NODE_ENV=development BUILD_PATH=dist node server/global",
69 - "dev:region": "NODE_ENV=development BUILD_PATH=dist nodemon --watch src --watch dist -- --experimental-loader ./loader/index.js --conditions=react-server server/region",
69 + "dev:global": "NODE_ENV=development BUILD_PATH=dist node --experimental-loader ./loader/global.js server/global",
70 + "dev:region": "NODE_ENV=development BUILD_PATH=dist nodemon --watch src --watch dist -- --experimental-loader ./loader/region.js --conditions=react-server server/region",
71 "start": "node scripts/build.js && concurrently \"npm run start:region\" \"npm run start:global\"",
71 - "start:global": "NODE_ENV=production node server/global",
72 - "start:region": "NODE_ENV=production node --experimental-loader ./loader/index.js --conditions=react-server server/region",
72 + "start:global": "NODE_ENV=production node --experimental-loader ./loader/global.js server/global",
73 + "start:region": "NODE_ENV=production node --experimental-loader ./loader/region.js --conditions=react-server server/region",
74 "build": "node scripts/build.js",
75 "test": "node scripts/test.js --env=jsdom"
76 },
fixtures/flight/public/index.html deleted
-11
@@ -1,11 +0,0 @@
1 -<!DOCTYPE html>
2 -<html lang="en">
3 - <head>
4 - <meta charset="utf-8" />
5 - <meta name="viewport" content="width=device-width, initial-scale=1" />
6 - <title>Flight</title>
7 - </head>
8 - <body>
9 - <div id="root"></div>
10 - </body>
11 -</html>
fixtures/flight/scripts/build.js
+1 -2
@@ -38,7 +38,7 @@ const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
38 const isInteractive = process.stdout.isTTY;
39
40 // Warn and crash if required files are missing
41 -if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
41 +if (!checkRequiredFiles([paths.appIndexJs])) {
42 process.exit(1);
43 }
44
@@ -204,6 +204,5 @@ function build(previousFileSizes) {
204 function copyPublicFolder() {
205 fs.copySync('public', 'build', {
206 dereference: true,
207 - filter: file => file !== paths.appHtml,
207 });
208 }
fixtures/flight/server/global.js
+87 -51
@@ -32,16 +32,45 @@ babelRegister({
32 // Ensure environment variables are read.
33 require('../config/env');
34
35 -const fs = require('fs');
35 +const fs = require('fs').promises;
36 const compress = require('compression');
37 const chalk = require('chalk');
38 const express = require('express');
39 -const app = express();
40 -
39 const http = require('http');
40
41 +const {renderToPipeableStream} = require('react-dom/server');
42 +const {createFromNodeStream} = require('react-server-dom-webpack/client');
43 +
44 +const app = express();
45 +
46 app.use(compress());
47
48 +if (process.env.NODE_ENV === 'development') {
49 + // In development we host the Webpack server for live bundling.
50 + const webpack = require('webpack');
51 + const webpackMiddleware = require('webpack-dev-middleware');
52 + const webpackHotMiddleware = require('webpack-hot-middleware');
53 + const paths = require('../config/paths');
54 + const configFactory = require('../config/webpack.config');
55 + const getClientEnvironment = require('../config/env');
56 +
57 + const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
58 +
59 + const config = configFactory('development');
60 + const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
61 + const appName = require(paths.appPackageJson).name;
62 +
63 + // Create a webpack compiler that is configured with custom messages.
64 + const compiler = webpack(config);
65 + app.use(
66 + webpackMiddleware(compiler, {
67 + publicPath: paths.publicUrlOrPath.slice(0, -1),
68 + serverSideRender: true,
69 + })
70 + );
71 + app.use(webpackHotMiddleware(compiler));
72 +}
73 +
74 function request(options, body) {
75 return new Promise((resolve, reject) => {
76 const req = http.request(options, res => {
@@ -55,12 +84,6 @@ function request(options, body) {
84 }
85
86 app.all('/', async function (req, res, next) {
58 - if (req.accepts('text/html')) {
59 - // Pass-through to the html file
60 - next();
61 - return;
62 - }
63 -
87 // Proxy the request to the regional server.
88 const proxiedHeaders = {
89 'X-Forwarded-Host': req.hostname,
@@ -85,52 +108,65 @@ app.all('/', async function (req, res, next) {
108 req
109 );
110
88 - try {
89 - const rscResponse = await promiseForData;
90 - res.set('Content-type', 'text/x-component');
91 - rscResponse.pipe(res);
92 - } catch (e) {
93 - console.error(`Failed to proxy request: ${e.stack}`);
94 - res.statusCode = 500;
95 - res.end();
111 + if (req.accepts('text/html')) {
112 + try {
113 + const rscResponse = await promiseForData;
114 +
115 + let virtualFs;
116 + let buildPath;
117 + if (process.env.NODE_ENV === 'development') {
118 + const {devMiddleware} = res.locals.webpack;
119 + virtualFs = devMiddleware.outputFileSystem.promises;
120 + buildPath = devMiddleware.stats.toJson().outputPath;
121 + } else {
122 + virtualFs = fs;
123 + buildPath = path.join(__dirname, '../build/');
124 + }
125 + // Read the module map from the virtual file system.
126 + const moduleMap = JSON.parse(
127 + await virtualFs.readFile(
128 + path.join(buildPath, 'react-ssr-manifest.json'),
129 + 'utf8'
130 + )
131 + );
132 + // Read the entrypoints containing the initial JS to bootstrap everything.
133 + // For other pages, the chunks in the RSC payload are enough.
134 + const mainJSChunks = JSON.parse(
135 + await virtualFs.readFile(
136 + path.join(buildPath, 'entrypoint-manifest.json'),
137 + 'utf8'
138 + )
139 + ).main.js;
140 + // For HTML, we're a "client" emulator that runs the client code,
141 + // so we start by consuming the RSC payload. This needs a module
142 + // map that reverse engineers the client-side path to the SSR path.
143 + const root = await createFromNodeStream(rscResponse, moduleMap);
144 + // Render it into HTML by resolving the client components
145 + res.set('Content-type', 'text/html');
146 + const {pipe} = renderToPipeableStream(root, {
147 + bootstrapScripts: mainJSChunks,
148 + });
149 + pipe(res);
150 + } catch (e) {
151 + console.error(`Failed to SSR: ${e.stack}`);
152 + res.statusCode = 500;
153 + res.end();
154 + }
155 + } else {
156 + try {
157 + const rscResponse = await promiseForData;
158 + // For other request, we pass-through the RSC payload.
159 + res.set('Content-type', 'text/x-component');
160 + rscResponse.pipe(res);
161 + } catch (e) {
162 + console.error(`Failed to proxy request: ${e.stack}`);
163 + res.statusCode = 500;
164 + res.end();
165 + }
166 }
167 });
168
169 if (process.env.NODE_ENV === 'development') {
100 - // In development we host the Webpack server for live bundling.
101 - const webpack = require('webpack');
102 - const webpackMiddleware = require('webpack-dev-middleware');
103 - const webpackHotMiddleware = require('webpack-hot-middleware');
104 - const paths = require('../config/paths');
105 - const configFactory = require('../config/webpack.config');
106 - const getClientEnvironment = require('../config/env');
107 -
108 - const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
109 -
110 - const HOST = '0.0.0.0';
111 - const PORT = 3000;
112 -
113 - const config = configFactory('development');
114 - const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
115 - const appName = require(paths.appPackageJson).name;
116 -
117 - // Create a webpack compiler that is configured with custom messages.
118 - const compiler = webpack(config);
119 - app.use(
120 - webpackMiddleware(compiler, {
121 - writeToDisk: filePath => {
122 - return /(react-client-manifest|react-ssr-manifest)\.json$/.test(
123 - filePath
124 - );
125 - },
126 - publicPath: paths.publicUrlOrPath.slice(0, -1),
127 - })
128 - );
129 - app.use(
130 - webpackHotMiddleware(compiler, {
131 - /* Options */
132 - })
133 - );
170 app.use(express.static('public'));
171 } else {
172 // In production we host the static build output.
fixtures/flight/server/region.js
+40 -7
@@ -50,14 +50,47 @@ app.get('/', async function (req, res) {
50 );
51 // const m = require('../src/App.js');
52 const m = await import('../src/App.js');
53 - const dist = process.env.NODE_ENV === 'development' ? 'dist' : 'build';
54 - const data = await readFile(
55 - path.resolve(__dirname, `../${dist}/react-client-manifest.json`),
56 - 'utf8'
57 - );
53 +
54 + let moduleMap;
55 + let mainCSSChunks;
56 + if (process.env.NODE_ENV === 'development') {
57 + // Read the module map from the HMR server in development.
58 + moduleMap = await (
59 + await fetch('http://localhost:3000/react-client-manifest.json')
60 + ).json();
61 + mainCSSChunks = (
62 + await (
63 + await fetch('http://localhost:3000/entrypoint-manifest.json')
64 + ).json()
65 + ).main.css;
66 + } else {
67 + // Read the module map from the static build in production.
68 + moduleMap = JSON.parse(
69 + await readFile(
70 + path.resolve(__dirname, `../build/react-client-manifest.json`),
71 + 'utf8'
72 + )
73 + );
74 + mainCSSChunks = JSON.parse(
75 + await readFile(
76 + path.resolve(__dirname, `../build/entrypoint-manifest.json`),
77 + 'utf8'
78 + )
79 + ).main.css;
80 + }
81 const App = m.default.default || m.default;
59 - const moduleMap = JSON.parse(data);
60 - const {pipe} = renderToPipeableStream(React.createElement(App), moduleMap);
82 + const root = [
83 + // Prepend the App's tree with stylesheets required for this entrypoint.
84 + mainCSSChunks.map(filename =>
85 + React.createElement('link', {
86 + rel: 'stylesheet',
87 + href: filename,
88 + precedence: 'default',
89 + })
90 + ),
91 + React.createElement(App),
92 + ];
93 + const {pipe} = renderToPipeableStream(root, moduleMap);
94 pipe(res);
95 });
96
fixtures/flight/src/App.js
+25 -16
@@ -14,21 +14,30 @@ export default async function App() {
14 const res = await fetch('http://localhost:3001/todos');
15 const todos = await res.json();
16 return (
17 - <Container>
18 - <h1>Hello, world</h1>
19 - <Counter />
20 - <Counter2 />
21 - <ul>
22 - {todos.map(todo => (
23 - <li key={todo.id}>{todo.text}</li>
24 - ))}
25 - </ul>
26 - <ShowMore>
27 - <p>Lorem ipsum</p>
28 - </ShowMore>
29 - <div>
30 - <Button action={like}>Like</Button>
31 - </div>
32 - </Container>
17 + <html lang="en">
18 + <head>
19 + <meta charSet="utf-8" />
20 + <meta name="viewport" content="width=device-width, initial-scale=1" />
21 + <title>Flight</title>
22 + </head>
23 + <body>
24 + <Container>
25 + <h1>Hello, world</h1>
26 + <Counter />
27 + <Counter2 />
28 + <ul>
29 + {todos.map(todo => (
30 + <li key={todo.id}>{todo.text}</li>
31 + ))}
32 + </ul>
33 + <ShowMore>
34 + <p>Lorem ipsum</p>
35 + </ShowMore>
36 + <div>
37 + <Button action={like}>Like</Button>
38 + </div>
39 + </Container>
40 + </body>
41 + </html>
42 );
43 }
fixtures/flight/src/index.js
+8 -5
@@ -3,6 +3,9 @@ import {Suspense} from 'react';
3 import ReactDOM from 'react-dom/client';
4 import ReactServerDOMReader from 'react-server-dom-webpack/client';
5
6 +// TODO: This should be a dependency of the App but we haven't implemented CSS in Node yet.
7 +import './style.css';
8 +
9 let data = ReactServerDOMReader.createFromFetch(
10 fetch('/', {
11 headers: {
@@ -24,12 +27,12 @@ let data = ReactServerDOMReader.createFromFetch(
27 }
28 );
29
30 +// TODO: Once not needed once children can be promises.
31 function Content() {
32 return React.use(data);
33 }
34
31 -ReactDOM.createRoot(document.getElementById('root')).render(
32 - <Suspense fallback={<h1>Loading...</h1>}>
33 - <Content />
34 - </Suspense>
35 -);
35 +// TODO: This transition shouldn't really be necessary but it is for now.
36 +React.startTransition(() => {
37 + ReactDOM.hydrateRoot(document, <Content />);
38 +});
fixtures/flight/src/style.css new
+3
@@ -0,0 +1,3 @@
1 +body {
2 + font-family: Helvetica;
3 +}
\ No newline at end of file
fixtures/flight/yarn.lock
+34 -13
@@ -7364,13 +7364,13 @@ react-dev-utils@^12.0.1:
7364 strip-ansi "^6.0.1"
7365 text-table "^0.2.0"
7366
7367 -react-dom@^18.2.0:
7368 - version "18.2.0"
7369 - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
7370 - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
7367 +react-dom@experimental:
7368 + version "0.0.0-experimental-6ff1733e6-20230225"
7369 + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-experimental-6ff1733e6-20230225.tgz#47c1e80f21230e6c650ba9e43d15fccd616441be"
7370 + integrity sha512-1vGCQDhmSOwBIb8QbTaSjBUysebPhl7WCcGuT6dW+HdlxFDvClL0M47K1e8kZWiuCkMUDjJaTlC4J32PgznbFw==
7371 dependencies:
7372 loose-envify "^1.1.0"
7373 - scheduler "^0.23.0"
7373 + scheduler "0.0.0-experimental-6ff1733e6-20230225"
7374
7375 react-error-overlay@^6.0.11:
7376 version "6.0.11"
@@ -7397,10 +7397,10 @@ react-refresh@^0.11.0:
7397 resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
7398 integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
7399
7400 -react@^18.2.0:
7401 - version "18.2.0"
7402 - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
7403 - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
7400 +react@experimental:
7401 + version "0.0.0-experimental-6ff1733e6-20230225"
7402 + resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-experimental-6ff1733e6-20230225.tgz#24bca9d60c6b3597f389e8bcd07e7b853dddc917"
7403 + integrity sha512-5syUtEwwbWzDHN84xNu9C9cciLW9sY4c19fG27EU5ApS1s+i7fFtS+KtyPMHU9S4eK0u65uQU3hWMqFvqzLHhw==
7404 dependencies:
7405 loose-envify "^1.1.0"
7406
@@ -7681,10 +7681,10 @@ saxes@^5.0.1:
7681 dependencies:
7682 xmlchars "^2.2.0"
7683
7684 -scheduler@^0.23.0:
7685 - version "0.23.0"
7686 - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
7687 - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
7684 +scheduler@0.0.0-experimental-6ff1733e6-20230225:
7685 + version "0.0.0-experimental-6ff1733e6-20230225"
7686 + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-experimental-6ff1733e6-20230225.tgz#9de04947f82afa784de799aa09aaad92ee67cb19"
7687 + integrity sha512-YDyRM4ohU6NmzsbiJ/UUdRB4ulz27n+3Su8LZ8cENrQDuu2us3sPN+y8CMGeqaNYJlS0GQTHNcLa2LZIK9BP5Q==
7688 dependencies:
7689 loose-envify "^1.1.0"
7690
@@ -7838,6 +7838,11 @@ slash@^4.0.0:
7838 resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
7839 integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
7840
7841 +source-list-map@^2.0.1:
7842 + version "2.0.1"
7843 + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
7844 + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
7845 +
7846 source-map-js@^1.0.1, source-map-js@^1.0.2:
7847 version "1.0.2"
7848 resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
@@ -8576,6 +8581,22 @@ webpack-hot-middleware@^2.25.3:
8581 html-entities "^2.1.0"
8582 strip-ansi "^6.0.0"
8583
8584 +webpack-manifest-plugin@^4.0.2:
8585 + version "4.1.1"
8586 + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz#10f8dbf4714ff93a215d5a45bcc416d80506f94f"
8587 + integrity sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==
8588 + dependencies:
8589 + tapable "^2.0.0"
8590 + webpack-sources "^2.2.0"
8591 +
8592 +webpack-sources@^2.2.0:
8593 + version "2.3.1"
8594 + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd"
8595 + integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==
8596 + dependencies:
8597 + source-list-map "^2.0.1"
8598 + source-map "^0.6.1"
8599 +
8600 webpack-sources@^3.2.3:
8601 version "3.2.3"
8602 resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js
+19 -20
@@ -256,30 +256,29 @@ export default class ReactFlightWebpackPlugin {
256 [string]: {specifier: string, name: string},
257 } = {};
258
259 - ssrManifest[id] = ssrExports;
260 -
261 - ['', '*']
262 - .concat(
263 - Array.isArray(moduleProvidedExports)
264 - ? moduleProvidedExports
265 - : [],
266 - )
267 - .forEach(function (name) {
268 - clientExports[name] = {
269 - id,
270 - chunks: chunkIds,
271 - name: name,
272 - };
273 - ssrExports[name] = {
274 - specifier: module.resource,
275 - name: name,
276 - };
277 - });
259 const href = pathToFileURL(module.resource).href;
260
261 if (href !== undefined) {
262 + ['', '*']
263 + .concat(
264 + Array.isArray(moduleProvidedExports)
265 + ? moduleProvidedExports
266 + : [],
267 + )
268 + .forEach(function (name) {
269 + clientExports[name] = {
270 + id,
271 + chunks: chunkIds,
272 + name: name,
273 + };
274 + ssrExports[name] = {
275 + specifier: href,
276 + name: name,
277 + };
278 + });
279 +
280 clientManifest[href] = clientExports;
282 - ssrManifest[href] = ssrExports;
281 + ssrManifest[id] = ssrExports;
282 }
283 }
284