[Flight] Assume `__turbopack_load_by_url__ ` returns a cached Promise (#33792)
Sebastian "Sebbie" Silbermann committed
Jul 16, 2025 at 13:20 UTC
d85ec5f5bd778d09214e3429e7fd043c4a152242
1 file changed
+15
-14
packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js
+15
-14
@@ -132,12 +132,6 @@ export function resolveServerReference<T>(
132
return [resolvedModuleData.id, resolvedModuleData.chunks, name];
133
}
134
135
-// The chunk cache contains all the chunks we've preloaded so far.
136
-// If they're still pending they're a thenable. This map also exists
137
-// in Turbopack but unfortunately it's not exposed so we have to
138
-// replicate it in user space. null means that it has already loaded.
139
-const chunkCache: Map<string, null | Promise<any>> = new Map();
140
-
135
function requireAsyncModule(id: string): null | Thenable<any> {
136
// We've already loaded all the chunks. We can require the module.
137
const promise = __turbopack_require__(id);
@@ -165,6 +159,13 @@ function requireAsyncModule(id: string): null | Thenable<any> {
159
}
160
}
161
162
+// Turbopack will return cached promises for the same chunk.
163
+// We still want to keep track of which chunks we have already instrumented
164
+// and which chunks have already been loaded until Turbopack returns instrumented
165
+// thenables directly.
166
+const instrumentedChunks: WeakSet<Thenable<any>> = new WeakSet();
167
+const loadedChunks: WeakSet<Thenable<any>> = new WeakSet();
168
+
169
function ignoreReject() {
170
// We rely on rejected promises to be handled by another listener.
171
}
@@ -174,19 +175,19 @@ export function preloadModule<T>(
175
metadata: ClientReference<T>,
176
): null | Thenable<any> {
177
const chunks = metadata[CHUNKS];
177
- const promises = [];
178
+ const promises: Promise<any>[] = [];
179
for (let i = 0; i < chunks.length; i++) {
180
const chunkFilename = chunks[i];
180
- const entry = chunkCache.get(chunkFilename);
181
- if (entry === undefined) {
182
- const thenable = loadChunk(chunkFilename);
181
+ const thenable = loadChunk(chunkFilename);
182
+ if (!loadedChunks.has(thenable)) {
183
promises.push(thenable);
184
+ }
185
+
186
+ if (!instrumentedChunks.has(thenable)) {
187
// $FlowFixMe[method-unbinding]
185
- const resolve = chunkCache.set.bind(chunkCache, chunkFilename, null);
188
+ const resolve = loadedChunks.add.bind(loadedChunks, thenable);
189
thenable.then(resolve, ignoreReject);
187
- chunkCache.set(chunkFilename, thenable);
188
- } else if (entry !== null) {
189
- promises.push(entry);
190
+ instrumentedChunks.add(thenable);
191
}
192
}
193
if (isAsyncImport(metadata)) {