|
1 | 1 | import { existsSync } from 'node:fs'
|
2 | 2 | import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
3 | 3 | import { join } from 'node:path'
|
| 4 | +import { join as posixJoin } from 'node:path/posix' |
4 | 5 |
|
5 | 6 | import { trace } from '@opentelemetry/api'
|
6 | 7 | import { wrapTracer } from '@opentelemetry/api/experimental'
|
@@ -41,17 +42,28 @@ const writeCacheEntry = async (
|
41 | 42 | }
|
42 | 43 |
|
43 | 44 | /**
|
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 |
45 | 46 | */
|
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 | +} |
47 | 58 |
|
48 | 59 | const buildPagesCacheValue = async (
|
49 | 60 | path: string,
|
50 | 61 | shouldUseEnumKind: boolean,
|
| 62 | + shouldSkipJson = false, |
51 | 63 | ): Promise<NetlifyCachedPageValue> => ({
|
52 | 64 | kind: shouldUseEnumKind ? 'PAGES' : 'PAGE',
|
53 | 65 | 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')), |
55 | 67 | headers: undefined,
|
56 | 68 | status: undefined,
|
57 | 69 | })
|
@@ -146,8 +158,8 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
|
146 | 158 | })
|
147 | 159 | : false
|
148 | 160 |
|
149 |
| - await Promise.all( |
150 |
| - Object.entries(manifest.routes).map( |
| 161 | + await Promise.all([ |
| 162 | + ...Object.entries(manifest.routes).map( |
151 | 163 | ([route, meta]): Promise<void> =>
|
152 | 164 | limitConcurrentPrerenderContentHandling(async () => {
|
153 | 165 | const lastModified = meta.initialRevalidateSeconds
|
@@ -195,7 +207,41 @@ export const copyPrerenderedContent = async (ctx: PluginContext): Promise<void>
|
195 | 207 | await writeCacheEntry(key, value, lastModified, ctx)
|
196 | 208 | }),
|
197 | 209 | ),
|
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 | + ]) |
199 | 245 |
|
200 | 246 | // app router 404 pages are not in the prerender manifest
|
201 | 247 | // so we need to check for them manually
|
|
0 commit comments