@samitouri / QOS-React / commits / 32b1fbad6f

[react-server-dom-turbopack] Support experimental array format for chunks (#37095)

Turbopack has introduced a new format for chunks (experimental): ```typescript type ChunkUrlOrMerged = ChunkUrl | [ChunkUrl, ChunkPath[], number[]] ``` This will allow us to dynamically choose to merge / unmerge chunks based on already loaded chunks. See https://github.com/vercel/next.js/pull/95261 for more context behind these feature. However, we noticed that this breaks `prepareDestinationWithChunks()` on server-side renders. The prepared chunk script tags were a stringified version of this array: <img width="1758" height="474" alt="Screenshot 2026-07-22 at 9 37 06 am" src="https://github.com/user-attachments/assets/fa273b44-5942-4037-b955-4338472242fe" /> Instead, we should prepare the merged chunk as these SSR-d pages would not have any loaded chunks tracked in memory (that happens on the client). This PR brings back this optimisation and also would remove the console-logged errors associated with them. --------- Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>

Sam Poder committed Jul 22, 2026 at 14:56 UTC 32b1fbad6fd1fc34ef361dcf06bcfb1ae3fed5f3
6 files changed +34 -12
packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopack.js
+3 -2
@@ -263,8 +263,9 @@ export function getModuleDebugInfo<T>(
263 const debugInfo: ReactDebugInfo = [];
264 let i = 0;
265 while (i < chunks.length) {
266 - const chunkFilename = chunks[i++];
267 - addChunkDebugInfo(debugInfo, chunkFilename);
266 + const chunk = chunks[i++];
267 + // A merged chunk is `[mergedChunkFilename, ...]`; use its own filename.
268 + addChunkDebugInfo(debugInfo, typeof chunk === 'string' ? chunk : chunk[0]);
269 }
270 return debugInfo;
271 }
packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackBrowser.js
+3 -1
@@ -13,7 +13,9 @@ import type {
13 ReactAsyncInfo,
14 } from 'shared/ReactTypes';
15
16 -export function loadChunk(filename: string): Promise<mixed> {
16 +import type {Chunk} from '../shared/ReactFlightImportMetadata';
17 +
18 +export function loadChunk(filename: Chunk): Promise<mixed> {
19 return __turbopack_load_by_url__(filename);
20 }
21
packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigBundlerTurbopackServer.js
+3 -1
@@ -9,7 +9,9 @@
9
10 import type {ReactDebugInfo} from 'shared/ReactTypes';
11
12 -export function loadChunk(filename: string): Promise<mixed> {
12 +import type {Chunk} from '../shared/ReactFlightImportMetadata';
13 +
14 +export function loadChunk(filename: Chunk): Promise<mixed> {
15 return __turbopack_load_by_url__(filename);
16 }
17
packages/react-server-dom-turbopack/src/client/ReactFlightClientConfigTargetTurbopackServer.js
+6 -3
@@ -9,6 +9,8 @@
9
10 import {preinitScriptForSSR} from 'react-client/src/ReactFlightClientConfig';
11
12 +import type {Chunk} from '../shared/ReactFlightImportMetadata';
13 +
14 export type ModuleLoading = null | {
15 prefix: string,
16 crossOrigin?: 'use-credentials' | '',
@@ -16,14 +18,15 @@ export type ModuleLoading = null | {
18
19 export function prepareDestinationWithChunks(
20 moduleLoading: ModuleLoading,
19 - // Chunks are single-indexed filenames
20 - chunks: Array<string>,
21 + chunks: Array<Chunk>,
22 nonce: ?string,
23 ) {
24 if (moduleLoading !== null) {
25 for (let i = 0; i < chunks.length; i++) {
26 + const chunk = chunks[i];
27 + // A merged chunk is `[mergedChunkFilename, ...]`; preinit its own URL.
28 preinitScriptForSSR(
26 - moduleLoading.prefix + chunks[i],
29 + moduleLoading.prefix + (typeof chunk === 'string' ? chunk : chunk[0]),
30 nonce,
31 moduleLoading.crossOrigin,
32 );
packages/react-server-dom-turbopack/src/shared/ReactFlightImportMetadata.js
+14 -4
@@ -7,10 +7,20 @@
7 * @flow
8 */
9
10 +// A chunk is either a plain chunk filename, or a merged chunk that bundles
11 +// several component chunks together, emitted as
12 +// `[mergedChunkFilename, componentChunkFilenames, componentChunkSizes]`.
13 +export type Chunk =
14 + | string
15 + | [
16 + /* merged chunk filename */ string,
17 + /* component chunk filenames */ Array<string>,
18 + /* component chunk sizes */ Array<number>,
19 + ];
20 +
21 export type ImportManifestEntry = {
22 id: string,
12 - // chunks is an array of filenames
13 - chunks: Array<string>,
23 + chunks: Array<Chunk>,
24 name: string,
25 async?: boolean,
26 };
@@ -20,11 +30,11 @@ export type ImportManifestEntry = {
30 export type ImportMetadata =
31 | [
32 /* id */ string,
23 - /* chunk filenames */ Array<string>,
33 + /* chunks */ Array<Chunk>,
34 /* name */ string,
35 /* async */ 1,
36 ]
27 - | [/* id */ string, /* chunk filenames */ Array<string>, /* name */ string];
37 + | [/* id */ string, /* chunks */ Array<Chunk>, /* name */ string];
38
39 export const ID = 0;
40 export const CHUNKS = 1;
scripts/flow/environment.js
+5 -1
@@ -146,7 +146,11 @@ declare const __webpack_require__: ((id: string) => any) & {
146 u: string => string,
147 };
148
149 -declare function __turbopack_load_by_url__(id: string): Promise<mixed>;
149 +// A chunk id is a plain filename, or a merged chunk emitted as
150 +// `[mergedChunkFilename, componentChunkFilenames, componentChunkSizes]`.
151 +declare function __turbopack_load_by_url__(
152 + id: string | [string, Array<string>, Array<number>],
153 +): Promise<mixed>;
154 declare const __turbopack_require__: ((id: string) => any) & {
155 u: string => string,
156 };