@@ -5,6 +5,7 @@ import { join } from 'node:path'
55import { trace } from '@opentelemetry/api'
66import { wrapTracer } from '@opentelemetry/api/experimental'
77import { glob } from 'fast-glob'
8+ import type { PrerenderManifestRoute } from 'next-with-cache-handler-v2/dist/build/index.js'
89import type { RouteMetadata } from 'next-with-cache-handler-v2/dist/export/routes/types.js'
910import pLimit from 'p-limit'
1011import { satisfies } from 'semver'
@@ -56,9 +57,34 @@ const routeToFilePath = (path: string) => {
5657 return `/${ path } `
5758}
5859
60+ function prerenderManifestRouteToRevalidateAndCacheControlProperties (
61+ prerenderManifestRoute : PrerenderManifestRoute | undefined ,
62+ ) {
63+ if ( ! prerenderManifestRoute ) {
64+ return { }
65+ }
66+
67+ return {
68+ revalidate : prerenderManifestRoute . initialRevalidateSeconds ,
69+ cacheControl : prerenderManifestRoute . initialRevalidateSeconds
70+ ? {
71+ revalidate : prerenderManifestRoute . initialRevalidateSeconds ,
72+ expire :
73+ typeof prerenderManifestRoute . initialExpireSeconds === 'number'
74+ ? prerenderManifestRoute . initialExpireSeconds +
75+ ( prerenderManifestRoute . initialExpireSeconds ===
76+ prerenderManifestRoute . initialRevalidateSeconds
77+ ? 31536000000
78+ : 0 )
79+ : undefined ,
80+ }
81+ : undefined ,
82+ }
83+ }
84+
5985const buildPagesCacheValue = async (
6086 path : string ,
61- initialRevalidateSeconds : number | false | undefined ,
87+ prerenderManifestRoute : PrerenderManifestRoute | undefined ,
6288 shouldUseEnumKind : boolean ,
6389 shouldSkipJson = false ,
6490) : Promise < NetlifyCachedPageValue > => ( {
@@ -67,14 +93,15 @@ const buildPagesCacheValue = async (
6793 pageData : shouldSkipJson ? { } : JSON . parse ( await readFile ( `${ path } .json` , 'utf-8' ) ) ,
6894 headers : undefined ,
6995 status : undefined ,
70- revalidate : initialRevalidateSeconds ,
96+ ... prerenderManifestRouteToRevalidateAndCacheControlProperties ( prerenderManifestRoute ) ,
7197} )
7298
7399const RSC_SEGMENTS_DIR_SUFFIX = '.segments'
74100const RSC_SEGMENT_SUFFIX = '.segment.rsc'
75101
76102const buildAppCacheValue = async (
77103 path : string ,
104+ prerenderManifestRoute : PrerenderManifestRoute | undefined ,
78105 shouldUseAppPageKind : boolean ,
79106 rscIsRequired = true ,
80107) : Promise < NetlifyCachedAppPageValue | NetlifyCachedPageValue > => {
@@ -117,6 +144,7 @@ const buildAppCacheValue = async (
117144 } ) ,
118145 segmentData,
119146 ...meta ,
147+ ...prerenderManifestRouteToRevalidateAndCacheControlProperties ( prerenderManifestRoute ) ,
120148 }
121149 }
122150
@@ -141,26 +169,37 @@ const buildAppCacheValue = async (
141169 html,
142170 pageData : rsc ,
143171 ...meta ,
172+ ...prerenderManifestRouteToRevalidateAndCacheControlProperties ( prerenderManifestRoute ) ,
144173 }
145174}
146175
147176const buildRouteCacheValue = async (
148177 path : string ,
149- initialRevalidateSeconds : number | false ,
178+ prerenderManifestRoute : PrerenderManifestRoute ,
150179 shouldUseEnumKind : boolean ,
151180) : Promise < NetlifyCachedRouteValue > => ( {
152181 kind : shouldUseEnumKind ? 'APP_ROUTE' : 'ROUTE' ,
153182 body : await readFile ( `${ path } .body` , 'base64' ) ,
154183 ...JSON . parse ( await readFile ( `${ path } .meta` , 'utf-8' ) ) ,
155- revalidate : initialRevalidateSeconds ,
184+ ... prerenderManifestRouteToRevalidateAndCacheControlProperties ( prerenderManifestRoute ) ,
156185} )
157186
158187const buildFetchCacheValue = async (
159188 path : string ,
160- ) : Promise < CachedFetchValueForMultipleVersions > => ( {
161- kind : 'FETCH' ,
162- ...JSON . parse ( await readFile ( path , 'utf-8' ) ) ,
163- } )
189+ ) : Promise < { value : CachedFetchValueForMultipleVersions ; lastModified : number } > => {
190+ const data = JSON . parse ( await readFile ( path , 'utf-8' ) ) as Omit <
191+ CachedFetchValueForMultipleVersions ,
192+ 'kind'
193+ >
194+
195+ return {
196+ value : {
197+ kind : 'FETCH' ,
198+ ...data ,
199+ } ,
200+ lastModified : Date . now ( ) - ( data ?. revalidate ?? 31536000000 ) ,
201+ }
202+ }
164203
165204/**
166205 * Upload prerendered content to the blob store
@@ -198,43 +237,44 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
198237
199238 await Promise . all ( [
200239 ...Object . entries ( manifest . routes ) . map (
201- ( [ route , meta ] ) : Promise < void > =>
240+ ( [ route , prerenderManifestRoute ] ) : Promise < void > =>
202241 limitConcurrentPrerenderContentHandling ( async ( ) => {
203- const lastModified = meta . initialRevalidateSeconds
204- ? Date . now ( ) - 31536000000
242+ const lastModified = prerenderManifestRoute . initialRevalidateSeconds
243+ ? Date . now ( ) - prerenderManifestRoute . initialRevalidateSeconds * 1000
205244 : Date . now ( )
206245 const key = routeToFilePath ( route )
207246 let value : NetlifyIncrementalCacheValue
208247 switch ( true ) {
209248 // Parallel route default layout has no prerendered page
210- case meta . dataRoute ?. endsWith ( '/default.rsc' ) &&
249+ case prerenderManifestRoute . dataRoute ?. endsWith ( '/default.rsc' ) &&
211250 ! existsSync ( join ( ctx . publishDir , 'server/app' , `${ key } .html` ) ) :
212251 return
213- case meta . dataRoute ?. endsWith ( '.json' ) :
252+ case prerenderManifestRoute . dataRoute ?. endsWith ( '.json' ) :
214253 if ( manifest . notFoundRoutes . includes ( route ) ) {
215254 // if pages router returns 'notFound: true', build won't produce html and json files
216255 return
217256 }
218257 value = await buildPagesCacheValue (
219258 join ( ctx . publishDir , 'server/pages' , key ) ,
220- meta . initialRevalidateSeconds ,
259+ prerenderManifestRoute ,
221260 shouldUseEnumKind ,
222261 )
223262 break
224- case meta . dataRoute ?. endsWith ( '.rsc' ) :
263+ case prerenderManifestRoute . dataRoute ?. endsWith ( '.rsc' ) :
225264 value = await buildAppCacheValue (
226265 join ( ctx . publishDir , 'server/app' , key ) ,
266+ prerenderManifestRoute ,
227267 shouldUseAppPageKind ,
228- meta . renderingMode !== 'PARTIALLY_STATIC' ,
268+ prerenderManifestRoute . renderingMode !== 'PARTIALLY_STATIC' ,
229269 )
230270 if ( route === '/_not-found' ) {
231271 appRouterNotFoundDefinedInPrerenderManifest = true
232272 }
233273 break
234- case meta . dataRoute === null :
274+ case prerenderManifestRoute . dataRoute === null :
235275 value = await buildRouteCacheValue (
236276 join ( ctx . publishDir , 'server/app' , key ) ,
237- meta . initialRevalidateSeconds ,
277+ prerenderManifestRoute ,
238278 shouldUseEnumKind ,
239279 )
240280 break
@@ -268,6 +308,7 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
268308 const key = routeToFilePath ( route )
269309 const value = await buildAppCacheValue (
270310 join ( ctx . publishDir , 'server/app' , key ) ,
311+ undefined ,
271312 shouldUseAppPageKind ,
272313 // shells always have `renderingMode === 'PARTIALLY_STATIC'`
273314 false ,
@@ -288,6 +329,7 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
288329 const key = '/404'
289330 const value = await buildAppCacheValue (
290331 join ( ctx . publishDir , 'server/app/_not-found' ) ,
332+ undefined ,
291333 shouldUseAppPageKind ,
292334 )
293335 await writeCacheEntry ( key , value , lastModified , ctx )
@@ -310,9 +352,8 @@ export const copyFetchContent = async (ctx: PluginContext): Promise<void> => {
310352
311353 await Promise . all (
312354 paths . map ( async ( key ) : Promise < void > => {
313- const lastModified = Date . now ( ) - 31536000000
314355 const path = join ( ctx . publishDir , 'cache/fetch-cache' , key )
315- const value = await buildFetchCacheValue ( path )
356+ const { value, lastModified } = await buildFetchCacheValue ( path )
316357 await writeCacheEntry ( key , value , lastModified , ctx )
317358 } ) ,
318359 )
0 commit comments