Skip to content

Commit 5b3234e

Browse files
committed
fix: create cache entries for fallback pages to support next@canary
1 parent 19e2281 commit 5b3234e

File tree

1 file changed

+52
-6
lines changed

1 file changed

+52
-6
lines changed

src/build/content/prerendered.ts

+52-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { existsSync } from 'node:fs'
22
import { mkdir, readFile, writeFile } from 'node:fs/promises'
33
import { join } from 'node:path'
4+
import { join as posixJoin } from 'node:path/posix'
45

56
import { trace } from '@opentelemetry/api'
67
import { wrapTracer } from '@opentelemetry/api/experimental'
@@ -41,17 +42,28 @@ const writeCacheEntry = async (
4142
}
4243

4344
/**
44-
* Normalize routes by stripping leading slashes and ensuring root path is index
45+
* Normalize routes by ensuring leading slashes and ensuring root path is index
4546
*/
46-
const routeToFilePath = (path: string) => (path === '/' ? '/index' : path)
47+
const routeToFilePath = (path: string) => {
48+
if (path === '/') {
49+
return '/index'
50+
}
51+
52+
if (path.startsWith('/')) {
53+
return path
54+
}
55+
56+
return `/${path}`
57+
}
4758

4859
const buildPagesCacheValue = async (
4960
path: string,
5061
shouldUseEnumKind: boolean,
62+
shouldSkipJson = false,
5163
): Promise<NetlifyCachedPageValue> => ({
5264
kind: shouldUseEnumKind ? 'PAGES' : 'PAGE',
5365
html: await readFile(`${path}.html`, 'utf-8'),
54-
pageData: JSON.parse(await readFile(`${path}.json`, 'utf-8')),
66+
pageData: shouldSkipJson ? {} : JSON.parse(await readFile(`${path}.json`, 'utf-8')),
5567
headers: undefined,
5668
status: undefined,
5769
})
@@ -146,8 +158,8 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
146158
})
147159
: false
148160

149-
await Promise.all(
150-
Object.entries(manifest.routes).map(
161+
await Promise.all([
162+
...Object.entries(manifest.routes).map(
151163
([route, meta]): Promise<void> =>
152164
limitConcurrentPrerenderContentHandling(async () => {
153165
const lastModified = meta.initialRevalidateSeconds
@@ -195,7 +207,41 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
195207
await writeCacheEntry(key, value, lastModified, ctx)
196208
}),
197209
),
198-
)
210+
...Object.entries(manifest.dynamicRoutes).map(async ([route, meta]) => {
211+
// fallback can be `string | false | null`
212+
// - `string` - when user use pages router with `fallback: true`, and then it's html file path
213+
// - `null` - when user use pages router with `fallback: 'block'` or app router with `export const dynamicParams = true`
214+
// - `false` - when user use pages router with `fallback: false` or app router with `export const dynamicParams = false`
215+
if (typeof meta.fallback === 'string') {
216+
// https://github.com/vercel/next.js/pull/68603 started using route cache to serve fallbacks
217+
// so we have to seed blobs with fallback entries
218+
219+
// create cache entry for pages router with `fallback: true` case
220+
await limitConcurrentPrerenderContentHandling(async () => {
221+
// dynamic routes don't have entries for each locale so we have to generate them
222+
// ourselves. If i18n is not used we use empty string as "locale" to be able to use
223+
// same handling wether i18n is used or not
224+
const locales = ctx.buildConfig.i18n?.locales ?? ['']
225+
226+
const lastModified = Date.now()
227+
for (const locale of locales) {
228+
const key = routeToFilePath(posixJoin(locale, route))
229+
const value = await buildPagesCacheValue(
230+
join(ctx.publishDir, 'server/pages', key),
231+
shouldUseEnumKind,
232+
true, // there is no corresponding json file for fallback, so we are skipping it for this entry
233+
)
234+
// Netlify Forms are not support and require a workaround
235+
if (value.kind === 'PAGE' || value.kind === 'PAGES' || value.kind === 'APP_PAGE') {
236+
verifyNetlifyForms(ctx, value.html)
237+
}
238+
239+
await writeCacheEntry(key, value, lastModified, ctx)
240+
}
241+
})
242+
}
243+
}),
244+
])
199245

200246
// app router 404 pages are not in the prerender manifest
201247
// so we need to check for them manually

0 commit comments

Comments
 (0)