diff --git a/.changeset/shaggy-pears-tell.md b/.changeset/shaggy-pears-tell.md new file mode 100644 index 000000000000..4cd6e97ad227 --- /dev/null +++ b/.changeset/shaggy-pears-tell.md @@ -0,0 +1,5 @@ +--- +"next": patch +--- + +Use `onPostpone` to determine if segment prefetch is partial diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 238f3175b3f8..3f127d1763dd 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -4258,27 +4258,8 @@ async function collectSegmentData( serverModuleMap: null, } - // When dynamicIO is enabled, missing data is encoded to an infinitely hanging - // promise, the absence of which we use to determine if a segment is fully - // static or partially static. However, when dynamicIO is not enabled, this - // trick doesn't work. - // - // So if PPR is enabled, and dynamicIO is not, we have to be conservative and - // assume all segments are partial. - // - // TODO: When PPR is on, we can at least optimize the case where the entire - // page is static. Either by passing that as an argument to this function, or - // by setting a header on the response like the we do for full page RSC - // prefetches today. The latter approach might be simpler since it requires - // less plumbing, and the client has to check the header regardless to see if - // PPR is enabled. - const shouldAssumePartialData = - renderOpts.experimental.isRoutePPREnabled === true && // PPR is enabled - !renderOpts.experimental.dynamicIO // dynamicIO is disabled - const staleTime = prerenderStore.stale return await ComponentMod.collectSegmentData( - shouldAssumePartialData, fullPageDataBuffer, staleTime, clientReferenceManifest.clientModules as ManifestNode, diff --git a/packages/next/src/server/app-render/collect-segment-data.tsx b/packages/next/src/server/app-render/collect-segment-data.tsx index 1ec7b2a07f25..d595b4d8799a 100644 --- a/packages/next/src/server/app-render/collect-segment-data.tsx +++ b/packages/next/src/server/app-render/collect-segment-data.tsx @@ -74,7 +74,6 @@ function onSegmentPrerenderError(error: unknown) { } export async function collectSegmentData( - shouldAssumePartialData: boolean, fullPageDataBuffer: Buffer, staleTime: number, clientModules: ManifestNode, @@ -119,7 +118,6 @@ export async function collectSegmentData( // inside of it, the side effects are transferred to the new stream. // @ts-expect-error - renderSegmentPrefetch( - shouldAssumePartialData, - buildId, - seedData, - key, - clientModules - ) + renderSegmentPrefetch(buildId, seedData, key, clientModules) ) ) } else { @@ -344,7 +330,6 @@ function encodeSegmentWithPossibleFallbackParam( } async function renderSegmentPrefetch( - shouldAssumePartialData: boolean, buildId: string, seedData: CacheNodeSeedData, key: string, @@ -359,8 +344,7 @@ async function renderSegmentPrefetch( buildId, rsc, loading, - isPartial: - shouldAssumePartialData || (await isPartialRSCData(rsc, clientModules)), + isPartial: await isPartialRSCData(rsc, clientModules), } // Since all we're doing is decoding and re-encoding a cached prerender, if // it takes longer than a microtask, it must because of hanging promises @@ -403,6 +387,11 @@ async function isPartialRSCData( await prerender(rsc, clientModules, { signal: abortController.signal, onError() {}, + onPostpone() { + // If something postponed, i.e. when Dynamic IO is not enabled, we can + // infer that the RSC data is partial. + isPartial = true + }, }) return isPartial } diff --git a/test/e2e/app-dir/segment-cache/client-only-opt-in/next.config.js b/test/e2e/app-dir/segment-cache/client-only-opt-in/next.config.js index 64a383e0b691..209191815e9e 100644 --- a/test/e2e/app-dir/segment-cache/client-only-opt-in/next.config.js +++ b/test/e2e/app-dir/segment-cache/client-only-opt-in/next.config.js @@ -4,7 +4,6 @@ const nextConfig = { experimental: { ppr: 'incremental', - dynamicIO: true, clientSegmentCache: 'client-only', }, } diff --git a/test/e2e/app-dir/segment-cache/export/next.config.js b/test/e2e/app-dir/segment-cache/export/next.config.js index e740e0c5233b..62ddfa93abf3 100644 --- a/test/e2e/app-dir/segment-cache/export/next.config.js +++ b/test/e2e/app-dir/segment-cache/export/next.config.js @@ -4,8 +4,6 @@ const nextConfig = { output: 'export', experimental: { - ppr: false, - dynamicIO: true, clientSegmentCache: true, }, } diff --git a/test/e2e/app-dir/segment-cache/incremental-opt-in/next.config.js b/test/e2e/app-dir/segment-cache/incremental-opt-in/next.config.js index ee74ac5cb97b..1fb210a73ee9 100644 --- a/test/e2e/app-dir/segment-cache/incremental-opt-in/next.config.js +++ b/test/e2e/app-dir/segment-cache/incremental-opt-in/next.config.js @@ -4,7 +4,6 @@ const nextConfig = { experimental: { ppr: 'incremental', - dynamicIO: true, clientSegmentCache: true, }, } diff --git a/test/ppr-tests-manifest.json b/test/ppr-tests-manifest.json index f0cf5b8e6b13..9a6cd1ab2df3 100644 --- a/test/ppr-tests-manifest.json +++ b/test/ppr-tests-manifest.json @@ -97,6 +97,7 @@ "test/e2e/app-dir/static-shell-debugging/static-shell-debugging.test.ts", "test/e2e/app-dir/dynamic-io-errors/dynamic-io-errors.prospective-fallback.test.ts", "test/e2e/app-dir/segment-cache/basic/segment-cache-basic.test.ts", + "test/e2e/app-dir/segment-cache/export/segment-cache-output-export.test.ts", "test/e2e/app-dir/segment-cache/incremental-opt-in/segment-cache-incremental-opt-in.test.ts", "test/e2e/app-dir/segment-cache/memory-pressure/segment-cache-memory-pressure.test.ts", "test/e2e/app-dir/segment-cache/prefetch-scheduling/prefetch-scheduling.test.ts",