From d85ec5f5bd778d09214e3429e7fd043c4a152242 Mon Sep 17 00:00:00 2001 From: "Sebastian \"Sebbie\" Silbermann" Date: Wed, 16 Jul 2025 13:20:10 +0200 Subject: [PATCH] [Flight] Assume `__turbopack_load_by_url__ ` returns a cached Promise (#33792) --- ...ReactFlightClientConfigBundlerTurbopack.js | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js index 8cd6804534e0a..fa20f032e0541 100644 --- a/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js +++ b/packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js @@ -132,12 +132,6 @@ export function resolveServerReference( return [resolvedModuleData.id, resolvedModuleData.chunks, name]; } -// The chunk cache contains all the chunks we've preloaded so far. -// If they're still pending they're a thenable. This map also exists -// in Turbopack but unfortunately it's not exposed so we have to -// replicate it in user space. null means that it has already loaded. -const chunkCache: Map> = new Map(); - function requireAsyncModule(id: string): null | Thenable { // We've already loaded all the chunks. We can require the module. const promise = __turbopack_require__(id); @@ -165,6 +159,13 @@ function requireAsyncModule(id: string): null | Thenable { } } +// Turbopack will return cached promises for the same chunk. +// We still want to keep track of which chunks we have already instrumented +// and which chunks have already been loaded until Turbopack returns instrumented +// thenables directly. +const instrumentedChunks: WeakSet> = new WeakSet(); +const loadedChunks: WeakSet> = new WeakSet(); + function ignoreReject() { // We rely on rejected promises to be handled by another listener. } @@ -174,19 +175,19 @@ export function preloadModule( metadata: ClientReference, ): null | Thenable { const chunks = metadata[CHUNKS]; - const promises = []; + const promises: Promise[] = []; for (let i = 0; i < chunks.length; i++) { const chunkFilename = chunks[i]; - const entry = chunkCache.get(chunkFilename); - if (entry === undefined) { - const thenable = loadChunk(chunkFilename); + const thenable = loadChunk(chunkFilename); + if (!loadedChunks.has(thenable)) { promises.push(thenable); + } + + if (!instrumentedChunks.has(thenable)) { // $FlowFixMe[method-unbinding] - const resolve = chunkCache.set.bind(chunkCache, chunkFilename, null); + const resolve = loadedChunks.add.bind(loadedChunks, thenable); thenable.then(resolve, ignoreReject); - chunkCache.set(chunkFilename, thenable); - } else if (entry !== null) { - promises.push(entry); + instrumentedChunks.add(thenable); } } if (isAsyncImport(metadata)) {