@@ -6,6 +6,7 @@ import { trace } from '@opentelemetry/api'
6
6
import { wrapTracer } from '@opentelemetry/api/experimental'
7
7
import { glob } from 'fast-glob'
8
8
import pLimit from 'p-limit'
9
+ import { satisfies } from 'semver'
9
10
10
11
import { encodeBlobKey } from '../../shared/blobkey.js'
11
12
import type {
@@ -53,41 +54,40 @@ const buildPagesCacheValue = async (path: string): Promise<NetlifyCachedPageValu
53
54
54
55
const buildAppCacheValue = async (
55
56
path : string ,
57
+ shouldUseAppPageKind : boolean ,
56
58
) : Promise < NetlifyCachedAppPageValue | NetlifyCachedPageValue > => {
57
59
const meta = JSON . parse ( await readFile ( `${ path } .meta` , 'utf-8' ) )
58
60
const html = await readFile ( `${ path } .html` , 'utf-8' )
59
61
60
- // TODO: check actual next version and not rely on env var set by tests
61
- const useOldStuff = process . env . NEXT_VERSION !== 'canary'
62
- if ( useOldStuff ) {
63
- const rsc = await readFile ( `${ path } .rsc` , 'utf-8' ) . catch ( ( ) =>
64
- readFile ( `${ path } .prefetch.rsc` , 'utf-8' ) ,
65
- )
66
-
67
- // Next < v14.2.0 does not set meta.status when notFound() is called directly on a page
68
- // Exclude Parallel routes, they are 404s when visited directly
69
- if (
70
- ! meta . status &&
71
- rsc . includes ( 'NEXT_NOT_FOUND' ) &&
72
- ! meta . headers [ 'x-next-cache-tags' ] . includes ( '/@' )
73
- ) {
74
- meta . status = 404
75
- }
62
+ // supporting both old and new cache kind for App Router pages - https://github.com/vercel/next.js/pull/65988
63
+ if ( shouldUseAppPageKind ) {
76
64
return {
77
- kind : 'PAGE ' ,
65
+ kind : 'APP_PAGE ' ,
78
66
html,
79
- pageData : rsc ,
67
+ rscData : await readFile ( `${ path } .rsc` , 'base64' ) . catch ( ( ) =>
68
+ readFile ( `${ path } .prefetch.rsc` , 'base64' ) ,
69
+ ) ,
80
70
...meta ,
81
71
}
82
72
}
83
73
84
- // use new stuff
74
+ const rsc = await readFile ( `${ path } .rsc` , 'utf-8' ) . catch ( ( ) =>
75
+ readFile ( `${ path } .prefetch.rsc` , 'utf-8' ) ,
76
+ )
77
+
78
+ // Next < v14.2.0 does not set meta.status when notFound() is called directly on a page
79
+ // Exclude Parallel routes, they are 404s when visited directly
80
+ if (
81
+ ! meta . status &&
82
+ rsc . includes ( 'NEXT_NOT_FOUND' ) &&
83
+ ! meta . headers [ 'x-next-cache-tags' ] . includes ( '/@' )
84
+ ) {
85
+ meta . status = 404
86
+ }
85
87
return {
86
- kind : 'APP_PAGE ' ,
88
+ kind : 'PAGE ' ,
87
89
html,
88
- rscData : await readFile ( `${ path } .rsc` , 'base64' ) . catch ( ( ) =>
89
- readFile ( `${ path } .prefetch.rsc` , 'base64' ) ,
90
- ) ,
90
+ pageData : rsc ,
91
91
...meta ,
92
92
}
93
93
}
@@ -120,6 +120,18 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
120
120
121
121
const limitConcurrentPrerenderContentHandling = pLimit ( 10 )
122
122
123
+ // https://github.com/vercel/next.js/pull/65988 introduced Cache kind specific to pages in App Router (`APP_PAGE`).
124
+ // Before this change there was common kind for both Pages router and App router pages
125
+ // so we check Next.js version to decide how to generate cache values for App Router pages.
126
+ // Note: at time of writing this code, released 15@rc uses old kind for App Router pages, while canaries after [email protected] use new kind.
127
+ // Looking at 15@rc release branch it was merging `canary` branch in, so the version constraint assume that future 15@rc (and 15@latest) versions
128
+ // will use new kind for App Router pages.
129
+ const shouldUseAppPageKind = ctx . nextVersion
130
+ ? satisfies ( ctx . nextVersion , '>=15.0.0-canary.13 <15.0.0-d || >15.0.0-rc.0' , {
131
+ includePrerelease : true ,
132
+ } )
133
+ : false
134
+
123
135
await Promise . all (
124
136
Object . entries ( manifest . routes ) . map (
125
137
( [ route , meta ] ) : Promise < void > =>
@@ -142,7 +154,10 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
142
154
value = await buildPagesCacheValue ( join ( ctx . publishDir , 'server/pages' , key ) )
143
155
break
144
156
case meta . dataRoute ?. endsWith ( '.rsc' ) :
145
- value = await buildAppCacheValue ( join ( ctx . publishDir , 'server/app' , key ) )
157
+ value = await buildAppCacheValue (
158
+ join ( ctx . publishDir , 'server/app' , key ) ,
159
+ shouldUseAppPageKind ,
160
+ )
146
161
break
147
162
case meta . dataRoute === null :
148
163
value = await buildRouteCacheValue (
@@ -164,7 +179,10 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
164
179
if ( existsSync ( join ( ctx . publishDir , `server/app/_not-found.html` ) ) ) {
165
180
const lastModified = Date . now ( )
166
181
const key = '/404'
167
- const value = await buildAppCacheValue ( join ( ctx . publishDir , 'server/app/_not-found' ) )
182
+ const value = await buildAppCacheValue (
183
+ join ( ctx . publishDir , 'server/app/_not-found' ) ,
184
+ shouldUseAppPageKind ,
185
+ )
168
186
await writeCacheEntry ( key , value , lastModified , ctx )
169
187
}
170
188
} catch ( error ) {
0 commit comments