From e0241b66005a7232fa2c2529afa1bd5b27bf8925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Sat, 4 Mar 2023 19:51:34 -0500 Subject: [PATCH] Simplify Webpack References by encoding file path + export name as single id (#26300) We always look up these references in a map so it doesn't matter what their value is. It could be a hash for example. The loaders now encode a single $$id instead of filepath + name. This changes the react-client-manifest to have a single level. The value inside the map is still split into module id + export name because that's what gets looked up in webpack. The react-ssr-manifest is still two levels because that's a reverse lookup. --- fixtures/flight/public/favicon.ico | Bin 0 -> 24838 bytes fixtures/flight/server/region.js | 4 +- fixtures/flight/src/index.js | 2 +- .../react-client/src/ReactFlightClient.js | 10 ++- .../ReactFlightDOMRelayServerHostConfig.js | 4 +- .../src/ReactFlightClientNodeBundlerConfig.js | 1 - .../src/ReactFlightDOMClientBrowser.js | 5 +- .../ReactFlightServerWebpackBundlerConfig.js | 35 +++----- .../src/ReactFlightWebpackNodeLoader.js | 6 +- .../src/ReactFlightWebpackNodeRegister.js | 78 +++++++----------- .../src/ReactFlightWebpackPlugin.js | 48 ++++++----- .../__tests__/ReactFlightDOMBrowser-test.js | 2 +- .../src/__tests__/ReactFlightDOMEdge-test.js | 4 +- .../src/__tests__/ReactFlightDOMNode-test.js | 4 +- .../src/__tests__/utils/WebpackMock.js | 60 ++++++-------- .../ReactFlightNativeRelayServerHostConfig.js | 4 +- .../react-server/src/ReactFlightServer.js | 8 +- .../ReactFlightServerBundlerConfigCustom.js | 2 +- 18 files changed, 120 insertions(+), 157 deletions(-) create mode 100644 fixtures/flight/public/favicon.ico diff --git a/fixtures/flight/public/favicon.ico b/fixtures/flight/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5c125de5d897c1ff5692a656485b3216123dcd89 GIT binary patch literal 24838 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: SomeChunk): (error: mixed) => void { function createServerReferenceProxy, T>( response: Response, - metaData: any, + metaData: {id: any, bound: Thenable>}, ): (...A) => Promise { const callServer = response._callServer; const proxy = function (): Promise { @@ -482,12 +482,14 @@ function createServerReferenceProxy, T>( const p = metaData.bound; if (p.status === INITIALIZED) { const bound = p.value; - return callServer(metaData, bound.concat(args)); + return callServer(metaData.id, bound.concat(args)); } // Since this is a fake Promise whose .then doesn't chain, we have to wrap it. // TODO: Remove the wrapper once that's fixed. - return Promise.resolve(p).then(function (bound) { - return callServer(metaData, bound.concat(args)); + return ((Promise.resolve(p): any): Promise>).then(function ( + bound, + ) { + return callServer(metaData.id, bound.concat(args)); }); }; return proxy; diff --git a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js index b0f41e156dd26..5d71f6e3c68e6 100644 --- a/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js +++ b/packages/react-server-dom-relay/src/ReactFlightDOMRelayServerHostConfig.js @@ -19,7 +19,7 @@ import isArray from 'shared/isArray'; export type ClientReference = JSResourceReference; export type ServerReference = T; -export type ServerReferenceMetadata = {}; +export type ServerReferenceId = {}; import type { Destination, @@ -69,7 +69,7 @@ export function resolveClientReferenceMetadata( export function resolveServerReferenceMetadata( config: BundlerConfig, resource: ServerReference, -): ServerReferenceMetadata { +): {id: ServerReferenceId, bound: Promise>} { throw new Error('Not implemented.'); } diff --git a/packages/react-server-dom-webpack/src/ReactFlightClientNodeBundlerConfig.js b/packages/react-server-dom-webpack/src/ReactFlightClientNodeBundlerConfig.js index a32f7e9596c4c..a7de70225c6d6 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightClientNodeBundlerConfig.js +++ b/packages/react-server-dom-webpack/src/ReactFlightClientNodeBundlerConfig.js @@ -25,7 +25,6 @@ export opaque type ClientReferenceMetadata = { id: string, chunks: Array, name: string, - async: boolean, }; // eslint-disable-next-line no-unused-vars diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMClientBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMClientBrowser.js index 5434876ee4946..ab24bf79f70e2 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMClientBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMClientBrowser.js @@ -20,10 +20,7 @@ import { close, } from 'react-client/src/ReactFlightClientStream'; -type CallServerCallback = ( - {filepath: string, name: string}, - args: A, -) => Promise; +type CallServerCallback = (string, args: A) => Promise; export type Options = { callServer?: CallServerCallback, diff --git a/packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js b/packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js index bfa27b4af8136..29d41e7f4ad98 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js +++ b/packages/react-server-dom-webpack/src/ReactFlightServerWebpackBundlerConfig.js @@ -10,32 +10,24 @@ import type {ReactModel} from 'react-server/src/ReactFlightServer'; type WebpackMap = { - [filepath: string]: { - [name: string]: ClientReferenceMetadata, - }, + [id: string]: ClientReferenceMetadata, }; export type BundlerConfig = WebpackMap; export type ServerReference = T & { $$typeof: symbol, - $$filepath: string, - $$name: string, + $$id: string, $$bound: Array, }; -export type ServerReferenceMetadata = { - id: string, - name: string, - bound: Promise>, -}; +export type ServerReferenceId = string; // eslint-disable-next-line no-unused-vars export type ClientReference = { $$typeof: symbol, - filepath: string, - name: string, - async: boolean, + $$id: string, + $$async: boolean, }; export type ClientReferenceMetadata = { @@ -53,12 +45,7 @@ const SERVER_REFERENCE_TAG = Symbol.for('react.server.reference'); export function getClientReferenceKey( reference: ClientReference, ): ClientReferenceKey { - return ( - reference.filepath + - '#' + - reference.name + - (reference.async ? '#async' : '') - ); + return reference.$$async ? reference.$$id + '#async' : reference.$$id; } export function isClientReference(reference: Object): boolean { @@ -73,9 +60,8 @@ export function resolveClientReferenceMetadata( config: BundlerConfig, clientReference: ClientReference, ): ClientReferenceMetadata { - const resolvedModuleData = - config[clientReference.filepath][clientReference.name]; - if (clientReference.async) { + const resolvedModuleData = config[clientReference.$$id]; + if (clientReference.$$async) { return { id: resolvedModuleData.id, chunks: resolvedModuleData.chunks, @@ -90,10 +76,9 @@ export function resolveClientReferenceMetadata( export function resolveServerReferenceMetadata( config: BundlerConfig, serverReference: ServerReference, -): ServerReferenceMetadata { +): {id: ServerReferenceId, bound: Promise>} { return { - id: serverReference.$$filepath, - name: serverReference.$$name, + id: serverReference.$$id, bound: Promise.resolve(serverReference.$$bound), }; } diff --git a/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js b/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js index 08e571f10c3de..5885cc98e65c6 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js +++ b/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeLoader.js @@ -187,8 +187,7 @@ function transformServerModule( } newSrc += 'Object.defineProperties(' + local + ',{'; newSrc += '$$typeof: {value: Symbol.for("react.server.reference")},'; - newSrc += '$$filepath: {value: ' + JSON.stringify(url) + '},'; - newSrc += '$$name: { value: ' + JSON.stringify(exported) + '},'; + newSrc += '$$id: {value: ' + JSON.stringify(url + '#' + exported) + '},'; newSrc += '$$bound: { value: [] }'; newSrc += '});\n'; }); @@ -343,9 +342,8 @@ async function transformClientModule( ');'; } newSrc += '},{'; - newSrc += 'name: { value: ' + JSON.stringify(name) + '},'; newSrc += '$$typeof: {value: CLIENT_REFERENCE},'; - newSrc += 'filepath: {value: ' + JSON.stringify(url) + '}'; + newSrc += '$$id: {value: ' + JSON.stringify(url + '#' + name) + '}'; newSrc += '});\n'; } return newSrc; diff --git a/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js b/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js index 7d4eb95ca2a05..be666af6fc369 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js +++ b/packages/react-server-dom-webpack/src/ReactFlightWebpackNodeRegister.js @@ -29,8 +29,7 @@ module.exports = function register() { // $FlowFixMe[method-unbinding] const args = Array.prototype.slice.call(arguments, 1); newFn.$$typeof = SERVER_REFERENCE; - newFn.$$filepath = this.$$filepath; - newFn.$$name = this.$$name; + newFn.$$id = this.$$id; newFn.$$bound = this.$$bound.concat(args); } return newFn; @@ -44,14 +43,14 @@ module.exports = function register() { // These names are a little too common. We should probably have a way to // have the Flight runtime extract the inner target instead. return target.$$typeof; - case 'filepath': - return target.filepath; + case '$$id': + return target.$$id; + case '$$async': + return target.$$async; case 'name': return target.name; case 'displayName': return undefined; - case 'async': - return target.async; // We need to special case this because createElement reads it if we pass this // reference. case 'defaultProps': @@ -69,20 +68,8 @@ module.exports = function register() { `that itself renders a Client Context Provider.`, ); } - let expression; - switch (target.name) { - case '': - // eslint-disable-next-line react-internal/safe-string-coercion - expression = String(name); - break; - case '*': - // eslint-disable-next-line react-internal/safe-string-coercion - expression = String(name); - break; - default: - // eslint-disable-next-line react-internal/safe-string-coercion - expression = String(target.name) + '.' + String(name); - } + // eslint-disable-next-line react-internal/safe-string-coercion + const expression = String(target.name) + '.' + String(name); throw new Error( `Cannot access ${expression} on the server. ` + 'You cannot dot into a client module from a server component. ' + @@ -103,15 +90,13 @@ module.exports = function register() { switch (name) { // These names are read by the Flight runtime if you end up using the exports object. case '$$typeof': - // These names are a little too common. We should probably have a way to - // have the Flight runtime extract the inner target instead. return target.$$typeof; - case 'filepath': - return target.filepath; + case '$$id': + return target.$$id; + case '$$async': + return target.$$async; case 'name': return target.name; - case 'async': - return target.async; // We need to special case this because createElement reads it if we pass this // reference. case 'defaultProps': @@ -125,7 +110,7 @@ module.exports = function register() { case '__esModule': // Something is conditionally checking which export to use. We'll pretend to be // an ESM compat module but then we'll check again on the client. - const moduleId = target.filepath; + const moduleId = target.$$id; target.default = Object.defineProperties( (function () { throw new Error( @@ -136,12 +121,11 @@ module.exports = function register() { ); }: any), { + $$typeof: {value: CLIENT_REFERENCE}, // This a placeholder value that tells the client to conditionally use the // whole object or just the default export. - name: {value: ''}, - $$typeof: {value: CLIENT_REFERENCE}, - filepath: {value: target.filepath}, - async: {value: target.async}, + $$id: {value: target.$$id + '#'}, + $$async: {value: target.$$async}, }, ); return true; @@ -150,17 +134,15 @@ module.exports = function register() { // Use a cached value return target.then; } - if (!target.async) { + if (!target.$$async) { // If this module is expected to return a Promise (such as an AsyncModule) then // we should resolve that with a client reference that unwraps the Promise on // the client. const clientReference = Object.defineProperties(({}: any), { - // Represents the whole Module object instead of a particular import. - name: {value: '*'}, $$typeof: {value: CLIENT_REFERENCE}, - filepath: {value: target.filepath}, - async: {value: true}, + $$id: {value: target.$$id}, + $$async: {value: true}, }); const proxy = new Proxy(clientReference, proxyHandlers); @@ -176,10 +158,9 @@ module.exports = function register() { // If this is not used as a Promise but is treated as a reference to a `.then` // export then we should treat it as a reference to that name. { - name: {value: 'then'}, $$typeof: {value: CLIENT_REFERENCE}, - filepath: {value: target.filepath}, - async: {value: false}, + $$id: {value: target.$$id + '#then'}, + $$async: {value: false}, }, )); return then; @@ -206,8 +187,8 @@ module.exports = function register() { { name: {value: name}, $$typeof: {value: CLIENT_REFERENCE}, - filepath: {value: target.filepath}, - async: {value: target.async}, + $$id: {value: target.$$id + '#' + name}, + $$async: {value: target.$$async}, }, ); cachedReference = target[name] = new Proxy( @@ -284,11 +265,10 @@ module.exports = function register() { if (useClient) { const moduleId: string = (url.pathToFileURL(filename).href: any); const clientReference = Object.defineProperties(({}: any), { - // Represents the whole Module object instead of a particular import. - name: {value: '*'}, $$typeof: {value: CLIENT_REFERENCE}, - filepath: {value: moduleId}, - async: {value: false}, + // Represents the whole Module object instead of a particular import. + $$id: {value: moduleId}, + $$async: {value: false}, }); // $FlowFixMe[incompatible-call] found when upgrading Flow this.exports = new Proxy(clientReference, proxyHandlers); @@ -306,10 +286,9 @@ module.exports = function register() { if (typeof exports === 'function') { // The module exports a function directly, Object.defineProperties((exports: any), { - // Represents the whole Module object instead of a particular import. $$typeof: {value: SERVER_REFERENCE}, - $$filepath: {value: moduleId}, - $$name: {value: '*'}, + // Represents the whole Module object instead of a particular import. + $$id: {value: moduleId}, $$bound: {value: []}, }); } else { @@ -320,8 +299,7 @@ module.exports = function register() { if (typeof value === 'function') { Object.defineProperties((value: any), { $$typeof: {value: SERVER_REFERENCE}, - $$filepath: {value: moduleId}, - $$name: {value: key}, + $$id: {value: moduleId + '#' + key}, $$bound: {value: []}, }); } diff --git a/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js b/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js index b8ca4768b96c2..5b19ee7d6f9a2 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js +++ b/packages/react-server-dom-webpack/src/ReactFlightWebpackPlugin.js @@ -220,9 +220,7 @@ export default class ReactFlightWebpackPlugin { } const clientManifest: { - [string]: { - [string]: {chunks: $FlowFixMe, id: string, name: string}, - }, + [string]: {chunks: $FlowFixMe, id: string, name: string}, } = {}; const ssrManifest: { [string]: { @@ -248,25 +246,35 @@ export default class ReactFlightWebpackPlugin { .getExportsInfo(module) .getProvidedExports(); - const clientExports: { - [string]: {chunks: $FlowFixMe, id: $FlowFixMe, name: string}, - } = {}; - - const ssrExports: { - [string]: {specifier: string, name: string}, - } = {}; - const href = pathToFileURL(module.resource).href; if (href !== undefined) { - ['', '*'] - .concat( - Array.isArray(moduleProvidedExports) - ? moduleProvidedExports - : [], - ) - .forEach(function (name) { - clientExports[name] = { + const ssrExports: { + [string]: {specifier: string, name: string}, + } = {}; + + clientManifest[href] = { + id, + chunks: chunkIds, + name: '*', + }; + ssrExports['*'] = { + specifier: href, + name: '*', + }; + clientManifest[href + '#'] = { + id, + chunks: chunkIds, + name: '', + }; + ssrExports[''] = { + specifier: href, + name: '', + }; + + if (Array.isArray(moduleProvidedExports)) { + moduleProvidedExports.forEach(function (name) { + clientManifest[href + '#' + name] = { id, chunks: chunkIds, name: name, @@ -276,8 +284,8 @@ export default class ReactFlightWebpackPlugin { name: name, }; }); + } - clientManifest[href] = clientExports; ssrManifest[id] = ssrExports; } } diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js index 157301ba58e0b..1365c7071dc0a 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js @@ -755,7 +755,7 @@ describe('ReactFlightDOMBrowser', () => { }); function requireServerRef(ref) { - const metaData = webpackServerMap[ref.id][ref.name]; + const metaData = webpackServerMap[ref]; const mod = __webpack_require__(metaData.id); if (metaData.name === '*') { return mod; diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js index a491e71096258..4cba5aa91405b 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMEdge-test.js @@ -61,12 +61,12 @@ describe('ReactFlightDOMEdge', () => { const ClientComponentOnTheServer = clientExports(ClientComponent); // In the SSR bundle this module won't exist. We simulate this by deleting it. - const clientId = webpackMap[ClientComponentOnTheClient.filepath]['*'].id; + const clientId = webpackMap[ClientComponentOnTheClient.$$id].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]['*']; + const ssrMetadata = webpackMap[ClientComponentOnTheServer.$$id]; const translationMap = { [clientId]: { '*': ssrMetadata, diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js index e5467a2f13cda..615cff2db9765 100644 --- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js +++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMNode-test.js @@ -67,12 +67,12 @@ describe('ReactFlightDOMNode', () => { const ClientComponentOnTheServer = clientExports(ClientComponent); // In the SSR bundle this module won't exist. We simulate this by deleting it. - const clientId = webpackMap[ClientComponentOnTheClient.filepath]['*'].id; + const clientId = webpackMap[ClientComponentOnTheClient.$$id].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]['*']; + const ssrMetadata = webpackMap[ClientComponentOnTheServer.$$id]; const translationMap = { [clientId]: { '*': ssrMetadata, diff --git a/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js b/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js index b208cfd030a90..a911c37602f5b 100644 --- a/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js +++ b/packages/react-server-dom-webpack/src/__tests__/utils/WebpackMock.js @@ -48,16 +48,14 @@ exports.clientModuleError = function clientModuleError(moduleError) { webpackErroredModules[idx] = moduleError; const path = url.pathToFileURL(idx).href; webpackClientMap[path] = { - '': { - id: idx, - chunks: [], - name: '', - }, - '*': { - id: idx, - chunks: [], - name: '*', - }, + id: idx, + chunks: [], + name: '*', + }; + webpackClientMap[path + '#'] = { + id: idx, + chunks: [], + name: '', }; const mod = {exports: {}}; nodeCompile.call(mod, '"use client"', idx); @@ -69,22 +67,20 @@ exports.clientExports = function clientExports(moduleExports) { webpackClientModules[idx] = moduleExports; const path = url.pathToFileURL(idx).href; webpackClientMap[path] = { - '': { - id: idx, - chunks: [], - name: '', - }, - '*': { - id: idx, - chunks: [], - name: '*', - }, + id: idx, + chunks: [], + name: '*', + }; + webpackClientMap[path + '#'] = { + id: idx, + chunks: [], + name: '', }; if (typeof moduleExports.then === 'function') { moduleExports.then( asyncModuleExports => { for (const name in asyncModuleExports) { - webpackClientMap[path][name] = { + webpackClientMap[path + '#' + name] = { id: idx, chunks: [], name: name, @@ -95,7 +91,7 @@ exports.clientExports = function clientExports(moduleExports) { ); } for (const name in moduleExports) { - webpackClientMap[path][name] = { + webpackClientMap[path + '#' + name] = { id: idx, chunks: [], name: name, @@ -112,19 +108,17 @@ exports.serverExports = function serverExports(moduleExports) { webpackServerModules[idx] = moduleExports; const path = url.pathToFileURL(idx).href; webpackServerMap[path] = { - '': { - id: idx, - chunks: [], - name: '', - }, - '*': { - id: idx, - chunks: [], - name: '*', - }, + id: idx, + chunks: [], + name: '*', + }; + webpackServerMap[path + '#'] = { + id: idx, + chunks: [], + name: '', }; for (const name in moduleExports) { - webpackServerMap[path][name] = { + webpackServerMap[path + '#' + name] = { id: idx, chunks: [], name: name, diff --git a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js index a317484597a00..703afb6e7ebc5 100644 --- a/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js +++ b/packages/react-server-native-relay/src/ReactFlightNativeRelayServerHostConfig.js @@ -16,7 +16,7 @@ import JSResourceReferenceImpl from 'JSResourceReferenceImpl'; export type ClientReference = JSResourceReference; export type ServerReference = T; -export type ServerReferenceMetadata = {}; +export type ServerReferenceId = {}; import type { Destination, @@ -66,7 +66,7 @@ export function resolveClientReferenceMetadata( export function resolveServerReferenceMetadata( config: BundlerConfig, resource: ServerReference, -): ServerReferenceMetadata { +): {id: ServerReferenceId, bound: Promise>} { throw new Error('Not implemented.'); } diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 20800530b831c..10aeb9e10723f 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -15,7 +15,7 @@ import type { ClientReference, ClientReferenceKey, ServerReference, - ServerReferenceMetadata, + ServerReferenceId, } from './ReactFlightServerConfig'; import type {ContextSnapshot} from './ReactFlightNewContext'; import type {ThenableState} from './ReactFlightThenable'; @@ -590,8 +590,10 @@ function serializeServerReference( if (existingId !== undefined) { return serializeServerReferenceID(existingId); } - const serverReferenceMetadata: ServerReferenceMetadata = - resolveServerReferenceMetadata(request.bundlerConfig, serverReference); + const serverReferenceMetadata: { + id: ServerReferenceId, + bound: Promise>, + } = resolveServerReferenceMetadata(request.bundlerConfig, serverReference); request.pendingChunks++; const metadataId = request.nextChunkId++; // We assume that this object doesn't suspend. diff --git a/packages/react-server/src/ReactFlightServerBundlerConfigCustom.js b/packages/react-server/src/ReactFlightServerBundlerConfigCustom.js index b53f0424c866f..edd34138db99d 100644 --- a/packages/react-server/src/ReactFlightServerBundlerConfigCustom.js +++ b/packages/react-server/src/ReactFlightServerBundlerConfigCustom.js @@ -13,7 +13,7 @@ export opaque type BundlerConfig = mixed; export opaque type ClientReference = mixed; // eslint-disable-line no-unused-vars export opaque type ServerReference = mixed; // eslint-disable-line no-unused-vars export opaque type ClientReferenceMetadata: any = mixed; -export opaque type ServerReferenceMetadata: any = mixed; +export opaque type ServerReferenceId: any = mixed; export opaque type ClientReferenceKey: any = mixed; export const isClientReference = $$$hostConfig.isClientReference; export const isServerReference = $$$hostConfig.isServerReference;