|
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