diff --git a/.changeset/spotty-hotels-train.md b/.changeset/spotty-hotels-train.md new file mode 100644 index 0000000000000..fea1a4e6b846c --- /dev/null +++ b/.changeset/spotty-hotels-train.md @@ -0,0 +1,5 @@ +--- +"next": patch +--- + +[Segment Cache] Fix: Ensure server references can be prerendered diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 9d8c2f12a5627..1a308647d985a 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -99,7 +99,10 @@ import { makeGetServerInsertedHTML } from './make-get-server-inserted-html' import { walkTreeWithFlightRouterState } from './walk-tree-with-flight-router-state' import { createComponentTree, getRootParams } from './create-component-tree' import { getAssetQueryString } from './get-asset-query-string' -import { setReferenceManifestsSingleton } from './encryption-utils' +import { + getServerModuleMap, + setReferenceManifestsSingleton, +} from './encryption-utils' import { DynamicState, type PostponedState, @@ -3872,7 +3875,7 @@ async function collectSegmentData( moduleMap: isEdgeRuntime ? clientReferenceManifest.edgeRscModuleMapping : clientReferenceManifest.rscModuleMapping, - serverModuleMap: null, + serverModuleMap: getServerModuleMap(), } const staleTime = prerenderStore.stale 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 d595b4d8799ad..5f84f969b4d76 100644 --- a/packages/next/src/server/app-render/collect-segment-data.tsx +++ b/packages/next/src/server/app-render/collect-segment-data.tsx @@ -199,9 +199,7 @@ async function PrefetchTreeData({ buildId, seedData, fallbackRouteParams, - fullPageDataBuffer, clientModules, - serverConsumerManifest, ROOT_SEGMENT_KEY, segmentTasks ) @@ -229,9 +227,7 @@ function collectSegmentDataImpl( buildId: string, seedData: CacheNodeSeedData | null, fallbackRouteParams: FallbackRouteParams | null, - fullPageDataBuffer: Buffer, clientModules: ManifestNode, - serverConsumerManifest: any, key: string, segmentTasks: Array> ): TreePrefetch { @@ -262,9 +258,7 @@ function collectSegmentDataImpl( buildId, childSeedData, fallbackRouteParams, - fullPageDataBuffer, clientModules, - serverConsumerManifest, childKey, segmentTasks ) diff --git a/test/e2e/app-dir/segment-cache/basic/app/with-server-action/page.tsx b/test/e2e/app-dir/segment-cache/basic/app/with-server-action/page.tsx new file mode 100644 index 0000000000000..a58efb42aa7bd --- /dev/null +++ b/test/e2e/app-dir/segment-cache/basic/app/with-server-action/page.tsx @@ -0,0 +1,19 @@ +import { LinkAccordion } from '../../components/link-accordion' + +export default function FullyStaticStart() { + return ( + <> +

+ This is a regression test case to ensure that prerendered segments that + include server actions do not throw an error when navigating to them. +

+ + + ) +} diff --git a/test/e2e/app-dir/segment-cache/basic/app/with-server-action/target-page/action.ts b/test/e2e/app-dir/segment-cache/basic/app/with-server-action/target-page/action.ts new file mode 100644 index 0000000000000..44895ef8824a5 --- /dev/null +++ b/test/e2e/app-dir/segment-cache/basic/app/with-server-action/target-page/action.ts @@ -0,0 +1,5 @@ +'use server' + +export async function action() { + console.log('Action!') +} diff --git a/test/e2e/app-dir/segment-cache/basic/app/with-server-action/target-page/page.tsx b/test/e2e/app-dir/segment-cache/basic/app/with-server-action/target-page/page.tsx new file mode 100644 index 0000000000000..8feccaf9e2649 --- /dev/null +++ b/test/e2e/app-dir/segment-cache/basic/app/with-server-action/target-page/page.tsx @@ -0,0 +1,9 @@ +import { action } from './action' + +export default function Target() { + return ( +
+ Target +
+ ) +} diff --git a/test/e2e/app-dir/segment-cache/basic/segment-cache-basic.test.ts b/test/e2e/app-dir/segment-cache/basic/segment-cache-basic.test.ts index 63039bb44480b..0ff2013869c82 100644 --- a/test/e2e/app-dir/segment-cache/basic/segment-cache-basic.test.ts +++ b/test/e2e/app-dir/segment-cache/basic/segment-cache-basic.test.ts @@ -306,4 +306,38 @@ describe('segment cache (basic tests)', () => { }, 'no-requests') expect(await readRandomNumberFromPage()).toBe(randomNumber3) }) + + it('does not throw an error when navigating to a page with a server action', async () => { + let act: ReturnType + const browser = await next.browser('/with-server-action', { + beforePageLoad(page) { + act = createRouterAct(page) + }, + }) + + // Reveal the link to trigger a prefetch. + const reveal = await browser.elementByCss('input[type="checkbox"]') + const link = await act( + async () => { + await reveal.click() + return await browser.elementByCss( + 'a[href="/with-server-action/target-page"]' + ) + }, + { includes: 'Target' } + ) + + await act( + async () => { + await link.click() + + // The page should render immediately because it was prefetched, and it + // should not throw an error. + const form = await browser.elementById('target-page') + expect(await form.innerHTML()).toBe('Target') + }, + // No additional requests were required, because everything was prefetched + 'no-requests' + ) + }) })