@@ -5,6 +5,7 @@ import { join } from 'node:path'
5
5
import { trace } from '@opentelemetry/api'
6
6
import { wrapTracer } from '@opentelemetry/api/experimental'
7
7
import { glob } from 'fast-glob'
8
+ import type { RouteMetadata } from 'next-with-cache-handler-v2/dist/export/routes/types.js'
8
9
import pLimit from 'p-limit'
9
10
import { satisfies } from 'semver'
10
11
@@ -69,21 +70,44 @@ const buildPagesCacheValue = async (
69
70
revalidate : initialRevalidateSeconds ,
70
71
} )
71
72
73
+ const RSC_SEGMENTS_DIR_SUFFIX = '.segments'
74
+ const RSC_SEGMENT_SUFFIX = '.segment.rsc'
75
+
72
76
const buildAppCacheValue = async (
73
77
path : string ,
74
78
shouldUseAppPageKind : boolean ,
75
79
) : Promise < NetlifyCachedAppPageValue | NetlifyCachedPageValue > => {
76
- const meta = JSON . parse ( await readFile ( `${ path } .meta` , 'utf-8' ) )
80
+ const meta = JSON . parse ( await readFile ( `${ path } .meta` , 'utf-8' ) ) as RouteMetadata
77
81
const html = await readFile ( `${ path } .html` , 'utf-8' )
78
82
79
83
// supporting both old and new cache kind for App Router pages - https://github.com/vercel/next.js/pull/65988
80
84
if ( shouldUseAppPageKind ) {
85
+ // segments are normalized and outputted separately for each segment, we denormalize it here and stitch
86
+ // fully constructed segmentData to avoid data fetch waterfalls later in cache handler at runtime
87
+ // https://github.com/vercel/next.js/blob/def2c6ba75dff754767379afb44c26c30bd3d96b/packages/next/src/server/lib/incremental-cache/file-system-cache.ts#L185
88
+ let segmentData : NetlifyCachedAppPageValue [ 'segmentData' ]
89
+ if ( meta . segmentPaths ) {
90
+ const segmentsDir = path + RSC_SEGMENTS_DIR_SUFFIX
91
+
92
+ segmentData = Object . fromEntries (
93
+ await Promise . all (
94
+ meta . segmentPaths . map ( async ( segmentPath : string ) => {
95
+ const segmentDataFilePath = segmentsDir + segmentPath + RSC_SEGMENT_SUFFIX
96
+
97
+ const segmentContent = await readFile ( segmentDataFilePath , 'base64' )
98
+ return [ segmentPath , segmentContent ]
99
+ } ) ,
100
+ ) ,
101
+ )
102
+ }
103
+
81
104
return {
82
105
kind : 'APP_PAGE' ,
83
106
html,
84
107
rscData : await readFile ( `${ path } .rsc` , 'base64' ) . catch ( ( ) =>
85
108
readFile ( `${ path } .prefetch.rsc` , 'base64' ) ,
86
109
) ,
110
+ segmentData,
87
111
...meta ,
88
112
}
89
113
}
@@ -97,7 +121,10 @@ const buildAppCacheValue = async (
97
121
if (
98
122
! meta . status &&
99
123
rsc . includes ( 'NEXT_NOT_FOUND' ) &&
100
- ! meta . headers [ 'x-next-cache-tags' ] . includes ( '/@' )
124
+ ! (
125
+ typeof meta . headers ?. [ 'x-next-cache-tags' ] === 'string' &&
126
+ meta . headers ?. [ 'x-next-cache-tags' ] . includes ( '/@' )
127
+ )
101
128
) {
102
129
meta . status = 404
103
130
}
0 commit comments