From 30cff9202e12807ef4d6cf16ebdc9b9245dea8cf Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Tue, 29 Apr 2025 16:07:21 -0400 Subject: [PATCH 1/4] fix: add a fallback for loadManifest import path --- src/run/handlers/cache.cts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index 512c62c994..a7c60cae55 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -176,7 +176,13 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { typeof cacheControl !== 'undefined') ) { try { - const { loadManifest } = await import('next/dist/server/load-manifest.js') + let loadManifest + try { + // Starting in 15.4.0-canary.10 loadManifest was relocated (https://github.com/vercel/next.js/pull/78358) + ({ loadManifest } = await import('next/dist/server/load-manifest.external.js')) + } catch { + ({ loadManifest } = await import('next/dist/server/load-manifest.js')) + } const prerenderManifest = loadManifest( join(this.options.serverDistDir, '..', 'prerender-manifest.json'), ) as PrerenderManifest From ef30a47620452e8851deff6f7ded5a2bf2d2c1f3 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Tue, 29 Apr 2025 16:21:11 -0400 Subject: [PATCH 2/4] chore: lint and typecheck fixes --- src/run/handlers/cache.cts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index a7c60cae55..b7c0677699 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -178,10 +178,12 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { try { let loadManifest try { - // Starting in 15.4.0-canary.10 loadManifest was relocated (https://github.com/vercel/next.js/pull/78358) - ({ loadManifest } = await import('next/dist/server/load-manifest.external.js')) + // @ts-expect-error Starting in 15.4.0-canary.10 loadManifest was relocated (https://github.com/vercel/next.js/pull/78358) + // eslint-disable-next-line @typescript-eslint/no-extra-semi, n/no-missing-import, import/no-unresolved + ;({ loadManifest } = await import('next/dist/server/load-manifest.external.js')) } catch { - ({ loadManifest } = await import('next/dist/server/load-manifest.js')) + // eslint-disable-next-line @typescript-eslint/no-extra-semi + ;({ loadManifest } = await import('next/dist/server/load-manifest.js')) } const prerenderManifest = loadManifest( join(this.options.serverDistDir, '..', 'prerender-manifest.json'), From 0494f72384896aca73296b2856c28c04b1551c98 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Tue, 29 Apr 2025 23:22:42 -0400 Subject: [PATCH 3/4] chore: add memoization for prerender manifest --- src/run/handlers/cache.cts | 39 +++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index b7c0677699..780f8595fc 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -32,6 +32,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { options: CacheHandlerContext revalidatedTags: string[] cacheStore: MemoizedKeyValueStoreBackedByRegionalBlobStore + prerenderManifest?: Promise tracer = getTracer() constructor(options: CacheHandlerContext) { @@ -165,6 +166,30 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { } } + private async getPrerenderManifest(serverDistDir: string): Promise { + if (this.prerenderManifest) { + return this.prerenderManifest + } + + const prerenderManifestPath = join(serverDistDir, '..', 'prerender-manifest.json') + + try { + // @ts-expect-error Starting in 15.4.0-canary.10 loadManifest was relocated (https://github.com/vercel/next.js/pull/78358) + // eslint-disable-next-line import/no-unresolved, n/no-missing-import + const { loadManifest } = await import('next/dist/server/load-manifest.external.js') + this.prerenderManifest = Promise.resolve( + loadManifest(prerenderManifestPath) as PrerenderManifest, + ) + } catch { + const { loadManifest } = await import('next/dist/server/load-manifest.js') + this.prerenderManifest = Promise.resolve( + loadManifest(prerenderManifestPath) as PrerenderManifest, + ) + } + + return this.prerenderManifest + } + private async injectEntryToPrerenderManifest( key: string, { revalidate, cacheControl }: Pick, @@ -176,19 +201,7 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { typeof cacheControl !== 'undefined') ) { try { - let loadManifest - try { - // @ts-expect-error Starting in 15.4.0-canary.10 loadManifest was relocated (https://github.com/vercel/next.js/pull/78358) - // eslint-disable-next-line @typescript-eslint/no-extra-semi, n/no-missing-import, import/no-unresolved - ;({ loadManifest } = await import('next/dist/server/load-manifest.external.js')) - } catch { - // eslint-disable-next-line @typescript-eslint/no-extra-semi - ;({ loadManifest } = await import('next/dist/server/load-manifest.js')) - } - const prerenderManifest = loadManifest( - join(this.options.serverDistDir, '..', 'prerender-manifest.json'), - ) as PrerenderManifest - + const prerenderManifest = await this.getPrerenderManifest(this.options.serverDistDir) if (typeof cacheControl !== 'undefined') { // instead of `revalidate` property, we might get `cacheControls` ( https://github.com/vercel/next.js/pull/76207 ) // then we need to keep track of revalidate values via SharedCacheControls From 2916c062c1e835998e798b037e4fce449e38dad6 Mon Sep 17 00:00:00 2001 From: Mateusz Bocian Date: Wed, 30 Apr 2025 10:52:46 -0400 Subject: [PATCH 4/4] fix: move prerenderManifest memoization to module scope --- src/run/handlers/cache.cts | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/run/handlers/cache.cts b/src/run/handlers/cache.cts index 780f8595fc..bfb86555ba 100644 --- a/src/run/handlers/cache.cts +++ b/src/run/handlers/cache.cts @@ -28,11 +28,12 @@ import { getLogger, getRequestContext } from './request-context.cjs' import { isAnyTagStale, markTagsAsStaleAndPurgeEdgeCache, purgeEdgeCache } from './tags-handler.cjs' import { getTracer, recordWarning } from './tracer.cjs' +let memoizedPrerenderManifest: PrerenderManifest + export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { options: CacheHandlerContext revalidatedTags: string[] cacheStore: MemoizedKeyValueStoreBackedByRegionalBlobStore - prerenderManifest?: Promise tracer = getTracer() constructor(options: CacheHandlerContext) { @@ -167,8 +168,8 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { } private async getPrerenderManifest(serverDistDir: string): Promise { - if (this.prerenderManifest) { - return this.prerenderManifest + if (memoizedPrerenderManifest) { + return memoizedPrerenderManifest } const prerenderManifestPath = join(serverDistDir, '..', 'prerender-manifest.json') @@ -177,17 +178,13 @@ export class NetlifyCacheHandler implements CacheHandlerForMultipleVersions { // @ts-expect-error Starting in 15.4.0-canary.10 loadManifest was relocated (https://github.com/vercel/next.js/pull/78358) // eslint-disable-next-line import/no-unresolved, n/no-missing-import const { loadManifest } = await import('next/dist/server/load-manifest.external.js') - this.prerenderManifest = Promise.resolve( - loadManifest(prerenderManifestPath) as PrerenderManifest, - ) + memoizedPrerenderManifest = loadManifest(prerenderManifestPath) as PrerenderManifest } catch { const { loadManifest } = await import('next/dist/server/load-manifest.js') - this.prerenderManifest = Promise.resolve( - loadManifest(prerenderManifestPath) as PrerenderManifest, - ) + memoizedPrerenderManifest = loadManifest(prerenderManifestPath) as PrerenderManifest } - return this.prerenderManifest + return memoizedPrerenderManifest } private async injectEntryToPrerenderManifest(