From e1bc1b262f01b1f9bcdc428c2add19bd15de1993 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 6 Aug 2024 12:59:32 -0600 Subject: [PATCH] refactor: cleanup incremental cache implementation, reduce FS access --- packages/next/src/build/utils.ts | 3 - .../helpers/create-incremental-cache.ts | 6 - packages/next/src/export/index.ts | 1 - packages/next/src/export/types.ts | 1 - packages/next/src/export/worker.ts | 2 - packages/next/src/server/base-server.ts | 61 ++-- packages/next/src/server/image-optimizer.ts | 11 +- .../lib/incremental-cache/fetch-cache.ts | 35 ++- .../incremental-cache/file-system-cache.ts | 274 +++++++++--------- .../src/server/lib/incremental-cache/index.ts | 36 +-- packages/next/src/server/lib/patch-fetch.ts | 12 +- packages/next/src/server/next-server.ts | 10 +- .../next/src/server/response-cache/index.ts | 38 ++- .../next/src/server/response-cache/types.ts | 43 ++- .../next/src/server/response-cache/utils.ts | 53 +++- packages/next/src/server/route-kind.ts | 5 + packages/next/src/server/web-server.ts | 2 - .../web/spec-extension/unstable-cache.ts | 17 +- .../file-system-cache.test.ts | 28 +- 19 files changed, 338 insertions(+), 300 deletions(-) diff --git a/packages/next/src/build/utils.ts b/packages/next/src/build/utils.ts index 9b121aad94cd6..455e4ba479bab 100644 --- a/packages/next/src/build/utils.ts +++ b/packages/next/src/build/utils.ts @@ -1374,9 +1374,6 @@ export async function buildAppStaticPaths({ const incrementalCache = new IncrementalCache({ fs: nodeFs, dev: true, - // Enabled both for build as we're only writing this cache, not reading it. - pagesDir: true, - appDir: true, flushToDisk: isrFlushToDisk, serverDistDir: path.join(distDir, 'server'), fetchCacheKeyPrefix, diff --git a/packages/next/src/export/helpers/create-incremental-cache.ts b/packages/next/src/export/helpers/create-incremental-cache.ts index 226630aa96520..f021a5e55687b 100644 --- a/packages/next/src/export/helpers/create-incremental-cache.ts +++ b/packages/next/src/export/helpers/create-incremental-cache.ts @@ -1,5 +1,3 @@ -import type { NextEnabledDirectories } from '../../server/base-server' - import path from 'path' import { IncrementalCache } from '../../server/lib/incremental-cache' import { hasNextSupport } from '../../telemetry/ci-info' @@ -13,7 +11,6 @@ export async function createIncrementalCache({ fetchCacheKeyPrefix, distDir, dir, - enabledDirectories, flushToDisk, }: { cacheHandler?: string @@ -21,7 +18,6 @@ export async function createIncrementalCache({ fetchCacheKeyPrefix?: string distDir: string dir: string - enabledDirectories: NextEnabledDirectories flushToDisk?: boolean }) { // Custom cache handler overrides. @@ -53,8 +49,6 @@ export async function createIncrementalCache({ notFoundRoutes: [], }), fs: nodeFs, - pagesDir: enabledDirectories.pages, - appDir: enabledDirectories.app, serverDistDir: path.join(distDir, 'server'), CurCacheHandler: CacheHandler, minimalMode: hasNextSupport, diff --git a/packages/next/src/export/index.ts b/packages/next/src/export/index.ts index 51af5e502dbe7..3e0764c6d8492 100644 --- a/packages/next/src/export/index.ts +++ b/packages/next/src/export/index.ts @@ -562,7 +562,6 @@ export async function exportAppImpl( fetchCacheKeyPrefix: nextConfig.experimental.fetchCacheKeyPrefix, cacheHandler: nextConfig.cacheHandler, enableExperimentalReact: needsExperimentalReact(nextConfig), - enabledDirectories, }) }) diff --git a/packages/next/src/export/types.ts b/packages/next/src/export/types.ts index 06afc7086a8de..b03f6886d8a11 100644 --- a/packages/next/src/export/types.ts +++ b/packages/next/src/export/types.ts @@ -66,7 +66,6 @@ export interface ExportPageInput { fetchCacheKeyPrefix?: string nextConfigOutput?: NextConfigComplete['output'] enableExperimentalReact?: boolean - enabledDirectories: NextEnabledDirectories } export type ExportedPageFile = { diff --git a/packages/next/src/export/worker.ts b/packages/next/src/export/worker.ts index 2324e65ba8385..f2d22b9269de9 100644 --- a/packages/next/src/export/worker.ts +++ b/packages/next/src/export/worker.ts @@ -71,7 +71,6 @@ async function exportPageImpl( enableExperimentalReact, ampValidatorPath, trailingSlash, - enabledDirectories, } = input if (enableExperimentalReact) { @@ -231,7 +230,6 @@ async function exportPageImpl( fetchCacheKeyPrefix, distDir, dir, - enabledDirectories, // skip writing to disk in minimal mode for now, pending some // changes to better support it flushToDisk: !hasNextSupport, diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index a4fca462b9e0c..d0688689f0883 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -15,13 +15,15 @@ import type { RenderOptsPartial as AppRenderOptsPartial, ServerOnInstrumentationRequestError, } from './app-render/types' -import type { - CachedAppPageValue, - CachedPageValue, - ServerComponentsHmrCache, - ResponseCacheBase, - ResponseCacheEntry, - ResponseGenerator, +import { + type CachedAppPageValue, + type CachedPageValue, + type ServerComponentsHmrCache, + type ResponseCacheBase, + type ResponseCacheEntry, + type ResponseGenerator, + CachedRouteKind, + type CachedRedirectValue, } from './response-cache' import type { UrlWithParsedQuery } from 'url' import { @@ -159,6 +161,8 @@ import { } from './after/builtin-request-context' import { ENCODED_TAGS } from './stream-utils/encodedTags' import { NextRequestHint } from './web/adapter' +import { RouteKind } from './route-kind' +import type { RouteModule } from './route-modules/route-module' export type FindComponentsResult = { components: LoadComponentsReturnType @@ -2043,7 +2047,10 @@ export default abstract class Server< ) } - const { routeModule } = components + let routeModule: RouteModule | undefined + if (components.routeModule) { + routeModule = components.routeModule + } /** * If the route being rendered is an app page, and the ppr feature has been @@ -2493,7 +2500,7 @@ export default abstract class Server< // Create the cache entry for the response. const cacheEntry: ResponseCacheEntry = { value: { - kind: 'ROUTE', + kind: CachedRouteKind.APP_ROUTE, status: response.status, body: Buffer.from(await blob.arrayBuffer()), headers, @@ -2657,9 +2664,9 @@ export default abstract class Server< if (metadata.isRedirect) { return { value: { - kind: 'REDIRECT', + kind: CachedRouteKind.REDIRECT, props: metadata.pageData ?? metadata.flightData, - }, + } satisfies CachedRedirectValue, revalidate: metadata.revalidate, } } @@ -2673,7 +2680,7 @@ export default abstract class Server< if (isAppPath) { return { value: { - kind: 'APP_PAGE', + kind: CachedRouteKind.APP_PAGE, html: result, headers, rscData: metadata.flightData, @@ -2686,7 +2693,7 @@ export default abstract class Server< return { value: { - kind: 'PAGE', + kind: CachedRouteKind.PAGES, html: result, pageData: metadata.pageData ?? metadata.flightData, headers, @@ -2802,11 +2809,11 @@ export default abstract class Server< return { value: { - kind: 'PAGE', + kind: CachedRouteKind.PAGES, html: RenderResult.fromStatic(html), + pageData: Object.create(null), status: undefined, headers: undefined, - pageData: {}, }, } } @@ -2845,12 +2852,12 @@ export default abstract class Server< return { revalidate: 1, value: { - kind: 'PAGE', + kind: CachedRouteKind.PAGES, html: RenderResult.fromStatic(''), pageData: {}, headers: undefined, status: undefined, - }, + } satisfies CachedPageValue, } } @@ -2871,7 +2878,11 @@ export default abstract class Server< ssgCacheKey, responseGenerator, { - routeKind: routeModule?.definition.kind, + routeKind: + // If the route module is not defined, we can assume it's a page being + // rendered and thus check isAppPath. + routeModule?.definition.kind ?? + (isAppPath ? RouteKind.APP_PAGE : RouteKind.PAGES), incrementalCache, isOnDemandRevalidate, isPrefetch: req.headers.purpose === 'prefetch', @@ -2892,7 +2903,7 @@ export default abstract class Server< } const didPostpone = - cacheEntry.value?.kind === 'APP_PAGE' && + cacheEntry.value?.kind === CachedRouteKind.APP_PAGE && typeof cacheEntry.value.postponed === 'string' if ( @@ -2924,7 +2935,7 @@ export default abstract class Server< const { value: cachedData } = cacheEntry // If the cache value is an image, we should error early. - if (cachedData?.kind === 'IMAGE') { + if (cachedData?.kind === CachedRouteKind.IMAGE) { throw new Error('invariant SSG should not return an image cache value') } @@ -3007,7 +3018,7 @@ export default abstract class Server< value: { ...cacheEntry.value, kind: - cacheEntry.value?.kind === 'APP_PAGE' + cacheEntry.value?.kind === CachedRouteKind.APP_PAGE ? 'PAGE' : cacheEntry.value?.kind, }, @@ -3049,7 +3060,7 @@ export default abstract class Server< } await this.render404(req, res, { pathname, query }, false) return null - } else if (cachedData.kind === 'REDIRECT') { + } else if (cachedData.kind === CachedRouteKind.REDIRECT) { if (cacheEntry.revalidate) { res.setHeader( 'Cache-Control', @@ -3073,7 +3084,7 @@ export default abstract class Server< await handleRedirect(cachedData.props) return null } - } else if (cachedData.kind === 'ROUTE') { + } else if (cachedData.kind === CachedRouteKind.APP_ROUTE) { const headers = { ...cachedData.headers } if (!(this.minimalMode && isSSG)) { @@ -3089,7 +3100,7 @@ export default abstract class Server< }) ) return null - } else if (cachedData.kind === 'APP_PAGE') { + } else if (cachedData.kind === CachedRouteKind.APP_PAGE) { // If the request has a postponed state and it's a resume request we // should error. if (cachedData.postponed && minimalPostponed) { @@ -3224,7 +3235,7 @@ export default abstract class Server< throw new Error('Invariant: expected a result to be returned') } - if (result.value?.kind !== 'APP_PAGE') { + if (result.value?.kind !== CachedRouteKind.APP_PAGE) { throw new Error( `Invariant: expected a page response, got ${result.value?.kind}` ) diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts index 4dfd153e848f9..ab28795eb0f85 100644 --- a/packages/next/src/server/image-optimizer.ts +++ b/packages/next/src/server/image-optimizer.ts @@ -14,9 +14,10 @@ import { hasMatch } from '../shared/lib/match-remote-pattern' import type { NextConfigComplete } from './config-shared' import { createRequestResponseMocks } from './lib/mock-request' import type { NextUrlWithParsedQuery } from './request-meta' -import type { - IncrementalCacheEntry, - IncrementalCacheValue, +import { + CachedRouteKind, + type IncrementalCacheEntry, + type IncrementalCacheValue, } from './response-cache' import { sendEtagResponse } from './send-payload' import { getContentType, getExtension } from './serve-static' @@ -354,7 +355,7 @@ export class ImageOptimizerCache { return { value: { - kind: 'IMAGE', + kind: CachedRouteKind.IMAGE, etag, buffer, extension, @@ -380,7 +381,7 @@ export class ImageOptimizerCache { revalidate?: number | false } ) { - if (value?.kind !== 'IMAGE') { + if (value?.kind !== CachedRouteKind.IMAGE) { throw new Error('invariant attempted to set non-image to image-cache') } diff --git a/packages/next/src/server/lib/incremental-cache/fetch-cache.ts b/packages/next/src/server/lib/incremental-cache/fetch-cache.ts index 909a3ad955a1a..538d628692382 100644 --- a/packages/next/src/server/lib/incremental-cache/fetch-cache.ts +++ b/packages/next/src/server/lib/incremental-cache/fetch-cache.ts @@ -1,5 +1,9 @@ import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './' -import type { IncrementalCacheValue } from '../../response-cache' +import { + CachedRouteKind, + IncrementalCacheKind, + type IncrementalCacheValue, +} from '../../response-cache' import LRUCache from 'next/dist/compiled/lru-cache' import { @@ -128,20 +132,22 @@ export default class FetchCache implements CacheHandler { length({ value }) { if (!value) { return 25 - } else if (value.kind === 'REDIRECT') { + } else if (value.kind === CachedRouteKind.REDIRECT) { return JSON.stringify(value.props).length - } else if (value.kind === 'IMAGE') { + } else if (value.kind === CachedRouteKind.IMAGE) { throw new Error('invariant image should not be incremental-cache') - } else if (value.kind === 'FETCH') { + } else if (value.kind === CachedRouteKind.FETCH) { return JSON.stringify(value.data || '').length - } else if (value.kind === 'ROUTE') { + } else if (value.kind === CachedRouteKind.APP_ROUTE) { return value.body.length } // rough estimate of size of cache value return ( value.html.length + (JSON.stringify( - value.kind === 'APP_PAGE' ? value.rscData : value.pageData + value.kind === CachedRouteKind.APP_PAGE + ? value.rscData + : value.pageData )?.length || 0) ) }, @@ -203,10 +209,10 @@ export default class FetchCache implements CacheHandler { } public async get(...args: Parameters) { - const [key, ctx = {}] = args - const { tags, softTags, kindHint, fetchIdx, fetchUrl } = ctx + const [key, ctx] = args + const { tags, softTags, kind: kindHint, fetchIdx, fetchUrl } = ctx - if (kindHint !== 'fetch') { + if (kindHint !== IncrementalCacheKind.FETCH) { return null } @@ -223,7 +229,7 @@ export default class FetchCache implements CacheHandler { let data = memoryCache?.get(key) const hasFetchKindAndMatchingTags = - data?.value?.kind === 'FETCH' && + data?.value?.kind === CachedRouteKind.FETCH && this.hasMatchingTags(tags ?? [], data.value.tags ?? []) // Get data from fetch cache. Also check if new tags have been @@ -274,13 +280,13 @@ export default class FetchCache implements CacheHandler { const cached: IncrementalCacheValue = await res.json() - if (!cached || cached.kind !== 'FETCH') { + if (!cached || cached.kind !== CachedRouteKind.FETCH) { DEBUG && console.log({ cached }) throw new Error('invalid cache value') } // if new tags were specified, merge those tags to the existing tags - if (cached.kind === 'FETCH') { + if (cached.kind === CachedRouteKind.FETCH) { cached.tags ??= [] for (const tag of tags ?? []) { if (!cached.tags.includes(tag)) { @@ -331,11 +337,12 @@ export default class FetchCache implements CacheHandler { public async set(...args: Parameters) { const [key, data, ctx] = args - const newValue = data?.kind === 'FETCH' ? data.data : undefined + const newValue = + data?.kind === CachedRouteKind.FETCH ? data.data : undefined const existingCache = memoryCache?.get(key) const existingValue = existingCache?.value if ( - existingValue?.kind === 'FETCH' && + existingValue?.kind === CachedRouteKind.FETCH && Object.keys(existingValue.data).every( (field) => JSON.stringify( diff --git a/packages/next/src/server/lib/incremental-cache/file-system-cache.ts b/packages/next/src/server/lib/incremental-cache/file-system-cache.ts index 5268fa4e66a9b..a017bd4dec695 100644 --- a/packages/next/src/server/lib/incremental-cache/file-system-cache.ts +++ b/packages/next/src/server/lib/incremental-cache/file-system-cache.ts @@ -1,7 +1,11 @@ import type { RouteMetadata } from '../../../export/routes/types' import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './' import type { CacheFs } from '../../../shared/lib/utils' -import type { CachedFetchValue } from '../../response-cache' +import { + CachedRouteKind, + IncrementalCacheKind, + type CachedFetchValue, +} from '../../response-cache' import LRUCache from 'next/dist/compiled/lru-cache' import path from '../../../shared/lib/isomorphic/path' @@ -32,8 +36,6 @@ export default class FileSystemCache implements CacheHandler { private fs: FileSystemCacheContext['fs'] private flushToDisk?: FileSystemCacheContext['flushToDisk'] private serverDistDir: FileSystemCacheContext['serverDistDir'] - private appDir: boolean - private pagesDir: boolean private tagsManifestPath?: string private revalidatedTags: string[] private debug: boolean @@ -42,8 +44,6 @@ export default class FileSystemCache implements CacheHandler { this.fs = ctx.fs this.flushToDisk = ctx.flushToDisk this.serverDistDir = ctx.serverDistDir - this.appDir = !!ctx._appDir - this.pagesDir = !!ctx._pagesDir this.revalidatedTags = ctx.revalidatedTags this.debug = !!process.env.NEXT_PRIVATE_DEBUG_CACHE @@ -58,20 +58,22 @@ export default class FileSystemCache implements CacheHandler { length({ value }) { if (!value) { return 25 - } else if (value.kind === 'REDIRECT') { + } else if (value.kind === CachedRouteKind.REDIRECT) { return JSON.stringify(value.props).length - } else if (value.kind === 'IMAGE') { + } else if (value.kind === CachedRouteKind.IMAGE) { throw new Error('invariant image should not be incremental-cache') - } else if (value.kind === 'FETCH') { + } else if (value.kind === CachedRouteKind.FETCH) { return JSON.stringify(value.data || '').length - } else if (value.kind === 'ROUTE') { + } else if (value.kind === CachedRouteKind.APP_ROUTE) { return value.body.length } // rough estimate of size of cache value return ( value.html.length + (JSON.stringify( - value.kind === 'APP_PAGE' ? value.rscData : value.pageData + value.kind === CachedRouteKind.APP_PAGE + ? value.rscData + : value.pageData )?.length || 0) ) }, @@ -171,59 +173,60 @@ export default class FileSystemCache implements CacheHandler { } public async get(...args: Parameters) { - const [key, ctx = {}] = args - const { tags, softTags, kindHint, isRoutePPREnabled } = ctx + const [key, ctx] = args + const { tags, softTags, kind, isRoutePPREnabled } = ctx + let data = memoryCache?.get(key) if (this.debug) { - console.log('get', key, tags, kindHint, !!data) + console.log('get', key, tags, kind, !!data) } // let's check the disk for seed data if (!data && process.env.NEXT_RUNTIME !== 'edge') { - try { - const filePath = this.getFilePath(`${key}.body`, 'app') - const fileData = await this.fs.readFile(filePath) - const { mtime } = await this.fs.stat(filePath) + if (kind === IncrementalCacheKind.APP_ROUTE) { + try { + const filePath = this.getFilePath( + `${key}.body`, + IncrementalCacheKind.APP_ROUTE + ) + const fileData = await this.fs.readFile(filePath) + const { mtime } = await this.fs.stat(filePath) - const meta = JSON.parse( - await this.fs.readFile( - filePath.replace(/\.body$/, NEXT_META_SUFFIX), - 'utf8' + const meta = JSON.parse( + await this.fs.readFile( + filePath.replace(/\.body$/, NEXT_META_SUFFIX), + 'utf8' + ) ) - ) - const cacheEntry: CacheHandlerValue = { - lastModified: mtime.getTime(), - value: { - kind: 'ROUTE', - body: fileData, - headers: meta.headers, - status: meta.status, - }, + const cacheEntry: CacheHandlerValue = { + lastModified: mtime.getTime(), + value: { + kind: CachedRouteKind.APP_ROUTE, + body: fileData, + headers: meta.headers, + status: meta.status, + }, + } + return cacheEntry + } catch { + return null } - return cacheEntry - } catch (_) { - // no .meta data for the related key } try { - // Determine the file kind if we didn't know it already. - let kind = kindHint - if (!kind) { - kind = this.detectFileKind(`${key}.html`) - } - - const isAppPath = kind === 'app' const filePath = this.getFilePath( - kind === 'fetch' ? key : `${key}.html`, + kind === IncrementalCacheKind.FETCH ? key : `${key}.html`, kind ) const fileData = await this.fs.readFile(filePath, 'utf8') const { mtime } = await this.fs.stat(filePath) - if (kind === 'fetch' && this.flushToDisk) { + if (kind === IncrementalCacheKind.FETCH) { + if (!this.flushToDisk) return null + const lastModified = mtime.getTime() const parsedData: CachedFetchValue = JSON.parse(fileData) data = { @@ -231,7 +234,7 @@ export default class FileSystemCache implements CacheHandler { value: parsedData, } - if (data.value?.kind === 'FETCH') { + if (data.value?.kind === CachedRouteKind.FETCH) { const storedTags = data.value?.tags // update stored tags if a new one is being added @@ -247,68 +250,80 @@ export default class FileSystemCache implements CacheHandler { }) } } - } else { + } else if (kind === IncrementalCacheKind.APP_PAGE) { let meta: RouteMetadata | undefined - if (isAppPath) { - try { - meta = JSON.parse( - await this.fs.readFile( - filePath.replace(/\.html$/, NEXT_META_SUFFIX), - 'utf8' - ) + // We try to load the metadata file, but if it fails, we don't + // error. + try { + meta = JSON.parse( + await this.fs.readFile( + filePath.replace(/\.html$/, NEXT_META_SUFFIX), + 'utf8' ) - } catch {} + ) + } catch {} + + const rscData = await this.fs.readFile( + this.getFilePath( + `${key}${isRoutePPREnabled ? RSC_PREFETCH_SUFFIX : RSC_SUFFIX}`, + IncrementalCacheKind.APP_PAGE + ) + ) + + data = { + lastModified: mtime.getTime(), + value: { + kind: CachedRouteKind.APP_PAGE, + html: fileData, + rscData, + postponed: meta?.postponed, + headers: meta?.headers, + status: meta?.status, + }, } + } else if (kind === IncrementalCacheKind.PAGES) { + let meta: RouteMetadata | undefined - if (isAppPath) { - const rscData = await this.fs.readFile( + const pageData = JSON.parse( + await this.fs.readFile( this.getFilePath( - `${key}${isRoutePPREnabled ? RSC_PREFETCH_SUFFIX : RSC_SUFFIX}`, - 'app' - ) - ) - data = { - lastModified: mtime.getTime(), - value: { - kind: 'APP_PAGE', - html: fileData, - rscData, - postponed: meta?.postponed, - headers: meta?.headers, - status: meta?.status, - }, - } - } else { - const pageData = JSON.parse( - await this.fs.readFile( - this.getFilePath(`${key}${NEXT_DATA_SUFFIX}`, 'pages'), - 'utf8' - ) + `${key}${NEXT_DATA_SUFFIX}`, + IncrementalCacheKind.PAGES + ), + 'utf8' ) + ) - data = { - lastModified: mtime.getTime(), - value: { - kind: 'PAGE', - html: fileData, - pageData, - headers: meta?.headers, - status: meta?.status, - }, - } + data = { + lastModified: mtime.getTime(), + value: { + kind: CachedRouteKind.PAGES, + html: fileData, + pageData, + headers: meta?.headers, + status: meta?.status, + }, } + } else { + console.trace('DEBUG', args) + throw new Error( + `Invariant: Unexpected route kind ${kind} in file system cache.` + ) } if (data) { memoryCache?.set(key, data) } - } catch (_) { - // unable to get data from disk + } catch { + return null } } - if (data?.value?.kind === 'APP_PAGE' || data?.value?.kind === 'PAGE') { + if ( + data?.value?.kind === CachedRouteKind.APP_PAGE || + data?.value?.kind === CachedRouteKind.PAGES + ) { let cacheTags: undefined | string[] const tagsHeader = data.value.headers?.[NEXT_CACHE_TAGS_HEADER] @@ -331,12 +346,10 @@ export default class FileSystemCache implements CacheHandler { // had a tag revalidated, if we want to be a background // revalidation instead we return data.lastModified = -1 if (isStale) { - data = undefined + return null } } - } - - if (data && data?.value?.kind === 'FETCH') { + } else if (data?.value?.kind === CachedRouteKind.FETCH) { await this.loadTagsManifest() const combinedTags = [...(tags || []), ...(softTags || [])] @@ -368,14 +381,18 @@ export default class FileSystemCache implements CacheHandler { value: data, lastModified: Date.now(), }) + if (this.debug) { console.log('set', key) } - if (!this.flushToDisk) return + if (!this.flushToDisk || !data) return - if (data?.kind === 'ROUTE') { - const filePath = this.getFilePath(`${key}.body`, 'app') + if (data.kind === CachedRouteKind.APP_ROUTE) { + const filePath = this.getFilePath( + `${key}.body`, + IncrementalCacheKind.APP_ROUTE + ) await this.fs.mkdir(path.dirname(filePath)) await this.fs.writeFile(filePath, data.body) @@ -389,14 +406,14 @@ export default class FileSystemCache implements CacheHandler { filePath.replace(/\.body$/, NEXT_META_SUFFIX), JSON.stringify(meta, null, 2) ) - return - } - - if (data?.kind === 'PAGE' || data?.kind === 'APP_PAGE') { - const isAppPath = data.kind === 'APP_PAGE' + } else if ( + data.kind === CachedRouteKind.PAGES || + data.kind === CachedRouteKind.APP_PAGE + ) { + const isAppPath = data.kind === CachedRouteKind.APP_PAGE const htmlPath = this.getFilePath( `${key}.html`, - isAppPath ? 'app' : 'pages' + isAppPath ? IncrementalCacheKind.APP_PAGE : IncrementalCacheKind.PAGES ) await this.fs.mkdir(path.dirname(htmlPath)) await this.fs.writeFile(htmlPath, data.html) @@ -410,7 +427,7 @@ export default class FileSystemCache implements CacheHandler { : RSC_SUFFIX : NEXT_DATA_SUFFIX }`, - isAppPath ? 'app' : 'pages' + isAppPath ? IncrementalCacheKind.APP_PAGE : IncrementalCacheKind.PAGES ), isAppPath ? data.rscData : JSON.stringify(data.pageData) ) @@ -427,8 +444,8 @@ export default class FileSystemCache implements CacheHandler { JSON.stringify(meta) ) } - } else if (data?.kind === 'FETCH') { - const filePath = this.getFilePath(key, 'fetch') + } else if (data.kind === CachedRouteKind.FETCH) { + const filePath = this.getFilePath(key, IncrementalCacheKind.FETCH) await this.fs.mkdir(path.dirname(filePath)) await this.fs.writeFile( filePath, @@ -437,49 +454,16 @@ export default class FileSystemCache implements CacheHandler { tags: ctx.tags, }) ) - } - } - - private detectFileKind(pathname: string) { - if (!this.appDir && !this.pagesDir) { + } else { throw new Error( - "Invariant: Can't determine file path kind, no page directory enabled" + `Invariant: Unexpected route kind ${data.kind} in file system cache.` ) } - - // If app directory isn't enabled, then assume it's pages and avoid the fs - // hit. - if (!this.appDir && this.pagesDir) { - return 'pages' - } - // Otherwise assume it's a pages file if the pages directory isn't enabled. - else if (this.appDir && !this.pagesDir) { - return 'app' - } - - // If both are enabled, we need to test each in order, starting with - // `pages`. - let filePath = this.getFilePath(pathname, 'pages') - if (this.fs.existsSync(filePath)) { - return 'pages' - } - - filePath = this.getFilePath(pathname, 'app') - if (this.fs.existsSync(filePath)) { - return 'app' - } - - throw new Error( - `Invariant: Unable to determine file path kind for ${pathname}` - ) } - private getFilePath( - pathname: string, - kind: 'app' | 'fetch' | 'pages' - ): string { + private getFilePath(pathname: string, kind: IncrementalCacheKind): string { switch (kind) { - case 'fetch': + case IncrementalCacheKind.FETCH: // we store in .next/cache/fetch-cache so it can be persisted // across deploys return path.join( @@ -489,12 +473,14 @@ export default class FileSystemCache implements CacheHandler { 'fetch-cache', pathname ) - case 'pages': + case IncrementalCacheKind.PAGES: return path.join(this.serverDistDir, 'pages', pathname) - case 'app': + case IncrementalCacheKind.IMAGE: + case IncrementalCacheKind.APP_PAGE: + case IncrementalCacheKind.APP_ROUTE: return path.join(this.serverDistDir, 'app', pathname) default: - throw new Error("Invariant: Can't determine file path kind") + throw new Error(`Unexpected file path kind: ${kind}`) } } } diff --git a/packages/next/src/server/lib/incremental-cache/index.ts b/packages/next/src/server/lib/incremental-cache/index.ts index 9f6966b425000..60e745e575268 100644 --- a/packages/next/src/server/lib/incremental-cache/index.ts +++ b/packages/next/src/server/lib/incremental-cache/index.ts @@ -1,10 +1,11 @@ import type { CacheFs } from '../../../shared/lib/utils' import type { PrerenderManifest } from '../../../build' -import type { - IncrementalCacheValue, - IncrementalCacheEntry, - IncrementalCache as IncrementalCacheType, - IncrementalCacheKindHint, +import { + type IncrementalCacheValue, + type IncrementalCacheEntry, + type IncrementalCache as IncrementalCacheType, + IncrementalCacheKind, + CachedRouteKind, } from '../../response-cache' import type { Revalidate } from '../revalidate' import type { DeepReadonly } from '../../../shared/lib/deep-readonly' @@ -31,8 +32,6 @@ export interface CacheHandlerContext { fetchCacheKeyPrefix?: string prerenderManifest?: PrerenderManifest revalidatedTags: string[] - _appDir: boolean - _pagesDir: boolean _requestHeaders: IncrementalCache['requestHeaders'] } @@ -90,8 +89,6 @@ export class IncrementalCache implements IncrementalCacheType { constructor({ fs, dev, - appDir, - pagesDir, flushToDisk, fetchCache, minimalMode, @@ -106,8 +103,6 @@ export class IncrementalCache implements IncrementalCacheType { }: { fs?: CacheFs dev: boolean - appDir?: boolean - pagesDir?: boolean fetchCache?: boolean minimalMode?: boolean serverDistDir?: string @@ -186,8 +181,6 @@ export class IncrementalCache implements IncrementalCacheType { serverDistDir, revalidatedTags, maxMemoryCacheSize, - _pagesDir: !!pagesDir, - _appDir: !!appDir, _requestHeaders: requestHeaders, fetchCacheKeyPrefix, }) @@ -384,33 +377,36 @@ export class IncrementalCache implements IncrementalCacheType { async get( cacheKey: string, ctx: { - kindHint?: IncrementalCacheKindHint + kind: IncrementalCacheKind revalidate?: Revalidate fetchUrl?: string fetchIdx?: number tags?: string[] softTags?: string[] isRoutePPREnabled?: boolean - } = {} + } ): Promise { // we don't leverage the prerender cache in dev mode // so that getStaticProps is always called for easier debugging if ( this.disableForTestmode || (this.dev && - (ctx.kindHint !== 'fetch' || + (ctx.kind !== IncrementalCacheKind.FETCH || this.requestHeaders['cache-control'] === 'no-cache')) ) { return null } - cacheKey = this._getPathname(cacheKey, ctx.kindHint === 'fetch') + cacheKey = this._getPathname( + cacheKey, + ctx.kind === IncrementalCacheKind.FETCH + ) let entry: IncrementalCacheEntry | null = null let revalidate = ctx.revalidate const cacheData = await this.cacheHandler?.get(cacheKey, ctx) - if (cacheData?.value?.kind === 'FETCH') { + if (cacheData?.value?.kind === CachedRouteKind.FETCH) { const combinedTags = [...(ctx.tags || []), ...(ctx.softTags || [])] // if a tag was revalidated we don't return stale data if ( @@ -430,7 +426,7 @@ export class IncrementalCache implements IncrementalCacheType { return { isStale: isStale, value: { - kind: 'FETCH', + kind: CachedRouteKind.FETCH, data, revalidate: revalidate, }, @@ -450,7 +446,7 @@ export class IncrementalCache implements IncrementalCacheType { revalidateAfter = this.calculateRevalidate( cacheKey, cacheData?.lastModified || Date.now(), - this.dev && ctx.kindHint !== 'fetch' + this.dev && ctx.kind !== IncrementalCacheKind.FETCH ) isStale = revalidateAfter !== false && revalidateAfter < Date.now() diff --git a/packages/next/src/server/lib/patch-fetch.ts b/packages/next/src/server/lib/patch-fetch.ts index 36d00b9294974..9a00b61ed869b 100644 --- a/packages/next/src/server/lib/patch-fetch.ts +++ b/packages/next/src/server/lib/patch-fetch.ts @@ -18,7 +18,11 @@ import type { RequestAsyncStorage, RequestStore, } from '../../client/components/request-async-storage.external' -import type { CachedFetchData } from '../response-cache' +import { + CachedRouteKind, + IncrementalCacheKind, + type CachedFetchData, +} from '../response-cache' const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge' @@ -598,7 +602,7 @@ function createPatchedFetcher( await staticGenerationStore.incrementalCache.set( cacheKey, { - kind: 'FETCH', + kind: CachedRouteKind.FETCH, data: cachedFetchData, revalidate: normalizedRevalidate, }, @@ -651,7 +655,7 @@ function createPatchedFetcher( const entry = staticGenerationStore.isOnDemandRevalidate ? null : await staticGenerationStore.incrementalCache.get(cacheKey, { - kindHint: 'fetch', + kind: IncrementalCacheKind.FETCH, revalidate: finalRevalidate, fetchUrl, fetchIdx, @@ -666,7 +670,7 @@ function createPatchedFetcher( cacheReasonOverride = 'cache-control: no-cache (hard refresh)' } - if (entry?.value && entry.value.kind === 'FETCH') { + if (entry?.value && entry.value.kind === CachedRouteKind.FETCH) { // when stale and is revalidating we wait for fresh data // so the revalidated entry has the updated data if (staticGenerationStore.isRevalidate && entry.isStale) { diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts index f7cb690d004b4..e22a88a226cce 100644 --- a/packages/next/src/server/next-server.ts +++ b/packages/next/src/server/next-server.ts @@ -74,7 +74,7 @@ import { removeTrailingSlash } from '../shared/lib/router/utils/remove-trailing- import { getNextPathnameInfo } from '../shared/lib/router/utils/get-next-pathname-info' import { getCloneableBody } from './body-streams' import { checkIsOnDemandRevalidate } from './api-utils' -import ResponseCache from './response-cache' +import ResponseCache, { CachedRouteKind } from './response-cache' import { IncrementalCache } from './lib/incremental-cache' import { normalizeAppPath } from '../shared/lib/router/utils/app-paths' @@ -101,6 +101,7 @@ import { formatDynamicImportPath } from '../lib/format-dynamic-import-path' import type { NextFontManifest } from '../build/webpack/plugins/next-font-manifest-plugin' import { isInterceptionRouteRewrite } from '../lib/generate-interception-routes-rewrites' import type { ServerOnInstrumentationRequestError } from './app-render/types' +import { RouteKind } from './route-kind' export * from './base-server' @@ -371,8 +372,6 @@ export default class NextNodeServer extends BaseServer< dev, requestHeaders, requestProtocol, - pagesDir: this.enabledDirectories.pages, - appDir: this.enabledDirectories.app, allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys, minimalMode: this.minimalMode, @@ -873,7 +872,7 @@ export default class NextNodeServer extends BaseServer< return { value: { - kind: 'IMAGE', + kind: CachedRouteKind.IMAGE, buffer, etag, extension: getExtension(contentType) as string, @@ -882,11 +881,12 @@ export default class NextNodeServer extends BaseServer< } }, { + routeKind: RouteKind.IMAGE, incrementalCache: imageOptimizerCache, } ) - if (cacheEntry?.value?.kind !== 'IMAGE') { + if (cacheEntry?.value?.kind !== CachedRouteKind.IMAGE) { throw new Error( 'invariant did not get entry from image response cache' ) diff --git a/packages/next/src/server/response-cache/index.ts b/packages/next/src/server/response-cache/index.ts index 49c10ba38d02c..df9eabab148a1 100644 --- a/packages/next/src/server/response-cache/index.ts +++ b/packages/next/src/server/response-cache/index.ts @@ -1,16 +1,20 @@ -import type { - IncrementalCache, - ResponseCacheEntry, - ResponseGenerator, - IncrementalCacheItem, - ResponseCacheBase, - IncrementalCacheKindHint, +import { + type IncrementalCache, + type ResponseCacheEntry, + type ResponseGenerator, + type IncrementalCacheItem, + type ResponseCacheBase, + CachedRouteKind, } from './types' -import { RouteKind } from '../route-kind' import { Batcher } from '../../lib/batcher' import { scheduleOnNextTick } from '../../lib/scheduler' -import { fromResponseCacheEntry, toResponseCacheEntry } from './utils' +import { + fromResponseCacheEntry, + routeKindToIncrementalCacheKind, + toResponseCacheEntry, +} from './utils' +import type { RouteKind } from '../route-kind' export * from './types' @@ -49,7 +53,7 @@ export default class ResponseCache implements ResponseCacheBase { key: string | null, responseGenerator: ResponseGenerator, context: { - routeKind?: RouteKind + routeKind: RouteKind isOnDemandRevalidate?: boolean isPrefetch?: boolean incrementalCache: IncrementalCache @@ -76,28 +80,20 @@ export default class ResponseCache implements ResponseCacheBase { } // Coerce the kindHint into a given kind for the incremental cache. - let kindHint: IncrementalCacheKindHint | undefined - if ( - context.routeKind === RouteKind.APP_PAGE || - context.routeKind === RouteKind.APP_ROUTE - ) { - kindHint = 'app' - } else if (context.routeKind === RouteKind.PAGES) { - kindHint = 'pages' - } + const kindHint = routeKindToIncrementalCacheKind(context.routeKind) let resolved = false let cachedResponse: IncrementalCacheItem = null try { cachedResponse = !this.minimalMode ? await incrementalCache.get(key, { - kindHint, + kind: kindHint, isRoutePPREnabled: context.isRoutePPREnabled, }) : null if (cachedResponse && !isOnDemandRevalidate) { - if (cachedResponse.value?.kind === 'FETCH') { + if (cachedResponse.value?.kind === CachedRouteKind.FETCH) { throw new Error( `invariant: unexpected cachedResponse of kind fetch in response cache` ) diff --git a/packages/next/src/server/response-cache/types.ts b/packages/next/src/server/response-cache/types.ts index f523a396067ad..b92cf2792974d 100644 --- a/packages/next/src/server/response-cache/types.ts +++ b/packages/next/src/server/response-cache/types.ts @@ -16,7 +16,7 @@ export interface ResponseCacheBase { * this is so it knows where to look up the cache entry from. If not * provided it will test the filesystem to check. */ - routeKind?: RouteKind + routeKind: RouteKind isRoutePPREnabled?: boolean } @@ -37,8 +37,17 @@ export type CachedFetchData = { status?: number } +export const enum CachedRouteKind { + APP_PAGE = 'APP_PAGE', + APP_ROUTE = 'APP_ROUTE', + PAGES = 'PAGES', + FETCH = 'FETCH', + REDIRECT = 'REDIRECT', + IMAGE = 'IMAGE', +} + export interface CachedFetchValue { - kind: 'FETCH' + kind: CachedRouteKind.FETCH data: CachedFetchData // tags are only present with file-system-cache // fetch cache stores tags outside of cache entry @@ -47,12 +56,12 @@ export interface CachedFetchValue { } export interface CachedRedirectValue { - kind: 'REDIRECT' + kind: CachedRouteKind.REDIRECT props: Object } export interface CachedAppPageValue { - kind: 'APP_PAGE' + kind: CachedRouteKind.APP_PAGE // this needs to be a RenderResult so since renderResponse // expects that type instead of a string html: RenderResult @@ -63,7 +72,7 @@ export interface CachedAppPageValue { } export interface CachedPageValue { - kind: 'PAGE' + kind: CachedRouteKind.PAGES // this needs to be a RenderResult so since renderResponse // expects that type instead of a string html: RenderResult @@ -73,7 +82,7 @@ export interface CachedPageValue { } export interface CachedRouteValue { - kind: 'ROUTE' + kind: CachedRouteKind.APP_ROUTE // this needs to be a RenderResult so since renderResponse // expects that type instead of a string body: Buffer @@ -82,7 +91,7 @@ export interface CachedRouteValue { } export interface CachedImageValue { - kind: 'IMAGE' + kind: CachedRouteKind.IMAGE etag: string buffer: Buffer extension: string @@ -91,7 +100,7 @@ export interface CachedImageValue { } export interface IncrementalCachedAppPageValue { - kind: 'APP_PAGE' + kind: CachedRouteKind.APP_PAGE // this needs to be a string since the cache expects to store // the string value html: string @@ -102,7 +111,7 @@ export interface IncrementalCachedAppPageValue { } export interface IncrementalCachedPageValue { - kind: 'PAGE' + kind: CachedRouteKind.PAGES // this needs to be a string since the cache expects to store // the string value html: string @@ -161,17 +170,19 @@ export type IncrementalCacheItem = { isMiss?: boolean } | null -export type IncrementalCacheKindHint = 'app' | 'pages' | 'fetch' +export const enum IncrementalCacheKind { + APP_PAGE = 'APP_PAGE', + APP_ROUTE = 'APP_ROUTE', + PAGES = 'PAGES', + FETCH = 'FETCH', + IMAGE = 'IMAGE', +} export interface IncrementalCache { get: ( key: string, - ctx?: { - /** - * The kind of cache entry to get. If not provided it will try to - * determine the kind from the filesystem. - */ - kindHint?: IncrementalCacheKindHint + ctx: { + kind: IncrementalCacheKind isRoutePPREnabled?: boolean } diff --git a/packages/next/src/server/response-cache/utils.ts b/packages/next/src/server/response-cache/utils.ts index 18ec64c1dd052..ca8a8e647b714 100644 --- a/packages/next/src/server/response-cache/utils.ts +++ b/packages/next/src/server/response-cache/utils.ts @@ -1,6 +1,14 @@ -import type { IncrementalCacheItem, ResponseCacheEntry } from './types' +import { + CachedRouteKind, + IncrementalCacheKind, + type CachedAppPageValue, + type CachedPageValue, + type IncrementalCacheItem, + type ResponseCacheEntry, +} from './types' import RenderResult from '../render-result' +import { RouteKind } from '../route-kind' export async function fromResponseCacheEntry( cacheEntry: ResponseCacheEntry @@ -8,17 +16,17 @@ export async function fromResponseCacheEntry( return { ...cacheEntry, value: - cacheEntry.value?.kind === 'PAGE' + cacheEntry.value?.kind === CachedRouteKind.PAGES ? { - kind: 'PAGE', + kind: CachedRouteKind.PAGES, html: await cacheEntry.value.html.toUnchunkedString(true), pageData: cacheEntry.value.pageData, headers: cacheEntry.value.headers, status: cacheEntry.value.status, } - : cacheEntry.value?.kind === 'APP_PAGE' + : cacheEntry.value?.kind === CachedRouteKind.APP_PAGE ? { - kind: 'APP_PAGE', + kind: CachedRouteKind.APP_PAGE, html: await cacheEntry.value.html.toUnchunkedString(true), postponed: cacheEntry.value.postponed, rscData: cacheEntry.value.rscData, @@ -34,7 +42,7 @@ export async function toResponseCacheEntry( ): Promise { if (!response) return null - if (response.value?.kind === 'FETCH') { + if (response.value?.kind === CachedRouteKind.FETCH) { throw new Error( 'Invariant: unexpected cachedResponse of kind fetch in response cache' ) @@ -45,23 +53,40 @@ export async function toResponseCacheEntry( isStale: response.isStale, revalidate: response.revalidate, value: - response.value?.kind === 'PAGE' - ? { - kind: 'PAGE', + response.value?.kind === CachedRouteKind.PAGES + ? ({ + kind: CachedRouteKind.PAGES, html: RenderResult.fromStatic(response.value.html), pageData: response.value.pageData, headers: response.value.headers, status: response.value.status, - } - : response.value?.kind === 'APP_PAGE' - ? { - kind: 'APP_PAGE', + } satisfies CachedPageValue) + : response.value?.kind === CachedRouteKind.APP_PAGE + ? ({ + kind: CachedRouteKind.APP_PAGE, html: RenderResult.fromStatic(response.value.html), rscData: response.value.rscData, headers: response.value.headers, status: response.value.status, postponed: response.value.postponed, - } + } satisfies CachedAppPageValue) : response.value, } } + +export function routeKindToIncrementalCacheKind( + routeKind: RouteKind +): IncrementalCacheKind { + switch (routeKind) { + case RouteKind.PAGES: + return IncrementalCacheKind.PAGES + case RouteKind.APP_PAGE: + return IncrementalCacheKind.APP_PAGE + case RouteKind.IMAGE: + return IncrementalCacheKind.IMAGE + case RouteKind.APP_ROUTE: + return IncrementalCacheKind.APP_ROUTE + default: + throw new Error(`Unexpected route kind ${routeKind}`) + } +} diff --git a/packages/next/src/server/route-kind.ts b/packages/next/src/server/route-kind.ts index 376f54e19b7e0..99f79ccdebff9 100644 --- a/packages/next/src/server/route-kind.ts +++ b/packages/next/src/server/route-kind.ts @@ -17,4 +17,9 @@ export const enum RouteKind { * filename of `route.{j,t}s{,x}`. */ APP_ROUTE = 'APP_ROUTE', + + /** + * `IMAGE` represents all the images that are generated by `next/image`. + */ + IMAGE = 'IMAGE', } diff --git a/packages/next/src/server/web-server.ts b/packages/next/src/server/web-server.ts index 12a5ac9e829a2..d1c2d7a2908a3 100644 --- a/packages/next/src/server/web-server.ts +++ b/packages/next/src/server/web-server.ts @@ -81,8 +81,6 @@ export default class NextWebServer extends BaseServer< dev, requestHeaders, requestProtocol: 'https', - pagesDir: this.enabledDirectories.pages, - appDir: this.enabledDirectories.app, allowedRevalidateHeaderKeys: this.nextConfig.experimental.allowedRevalidateHeaderKeys, minimalMode: this.minimalMode, diff --git a/packages/next/src/server/web/spec-extension/unstable-cache.ts b/packages/next/src/server/web/spec-extension/unstable-cache.ts index 6f5ecdcecde2b..b0ee8e873a823 100644 --- a/packages/next/src/server/web/spec-extension/unstable-cache.ts +++ b/packages/next/src/server/web/spec-extension/unstable-cache.ts @@ -8,6 +8,11 @@ import { } from '../../lib/patch-fetch' import { staticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage.external' import { requestAsyncStorage } from '../../../client/components/request-async-storage.external' +import { + CachedRouteKind, + IncrementalCacheKind, + type CachedFetchData, +} from '../../response-cache' type Callback = (...args: any[]) => Promise @@ -25,14 +30,14 @@ async function cacheNewResult( await incrementalCache.set( cacheKey, { - kind: 'FETCH', + kind: CachedRouteKind.FETCH, data: { headers: {}, // TODO: handle non-JSON values? body: JSON.stringify(result), status: 200, url: '', - }, + } satisfies CachedFetchData, revalidate: typeof revalidate !== 'number' ? CACHE_ONE_YEAR : revalidate, }, { @@ -185,7 +190,7 @@ export function unstable_cache( ) { // We attempt to get the current cache entry from the incremental cache. const cacheEntry = await incrementalCache.get(cacheKey, { - kindHint: 'fetch', + kind: IncrementalCacheKind.FETCH, revalidate: options.revalidate, tags, softTags: implicitTags, @@ -195,7 +200,7 @@ export function unstable_cache( if (cacheEntry && cacheEntry.value) { // The entry exists and has a value - if (cacheEntry.value.kind !== 'FETCH') { + if (cacheEntry.value.kind !== CachedRouteKind.FETCH) { // The entry is invalid and we need a special warning // @TODO why do we warn this way? Should this just be an error? How are these errors surfaced // so bugs can be reported @@ -298,7 +303,7 @@ export function unstable_cache( addImplicitTags(staticGenerationStore, requestStore) const cacheEntry = await incrementalCache.get(cacheKey, { - kindHint: 'fetch', + kind: IncrementalCacheKind.FETCH, revalidate: options.revalidate, tags, fetchIdx, @@ -308,7 +313,7 @@ export function unstable_cache( if (cacheEntry && cacheEntry.value) { // The entry exists and has a value - if (cacheEntry.value.kind !== 'FETCH') { + if (cacheEntry.value.kind !== CachedRouteKind.FETCH) { // The entry is invalid and we need a special warning // @TODO why do we warn this way? Should this just be an error? How are these errors surfaced // so bugs can be reported diff --git a/test/unit/incremental-cache/file-system-cache.test.ts b/test/unit/incremental-cache/file-system-cache.test.ts index 7ba2c1624e21c..64a1c53d96893 100644 --- a/test/unit/incremental-cache/file-system-cache.test.ts +++ b/test/unit/incremental-cache/file-system-cache.test.ts @@ -2,14 +2,16 @@ import { promises as fs } from 'node:fs' import { fileURLToPath } from 'node:url' import FileSystemCache from 'next/dist/server/lib/incremental-cache/file-system-cache' import { nodeFs } from 'next/dist/server/lib/node-fs-methods' +import { + CachedRouteKind, + IncrementalCacheKind, +} from 'next/dist/server/response-cache' const cacheDir = fileURLToPath(new URL('./cache', import.meta.url)) describe('FileSystemCache', () => { it('set image route', async () => { const fsCache = new FileSystemCache({ - _appDir: true, - _pagesDir: true, _requestHeaders: {}, flushToDisk: true, fs: nodeFs, @@ -29,26 +31,30 @@ describe('FileSystemCache', () => { 'Content-Type': 'image/png', }, status: 200, - kind: 'ROUTE', + kind: CachedRouteKind.APP_ROUTE, }, {} ) - expect((await fsCache.get('icon.png'))?.value).toEqual({ + expect( + ( + await fsCache.get('icon.png', { + kind: IncrementalCacheKind.APP_ROUTE, + }) + )?.value + ).toEqual({ body: binary, headers: { 'Content-Type': 'image/png', }, status: 200, - kind: 'ROUTE', + kind: IncrementalCacheKind.APP_ROUTE, }) }) }) describe('FileSystemCache (isrMemory 0)', () => { const fsCache = new FileSystemCache({ - _appDir: true, - _pagesDir: true, _requestHeaders: {}, flushToDisk: true, fs: nodeFs, @@ -61,7 +67,7 @@ describe('FileSystemCache (isrMemory 0)', () => { await fsCache.set( 'fetch-cache', { - kind: 'FETCH', + kind: CachedRouteKind.FETCH, data: { headers: {}, body: 'MTcwMDA1NjM4MQ==', @@ -81,7 +87,7 @@ describe('FileSystemCache (isrMemory 0)', () => { const res = await fsCache.get('fetch-cache', { tags: ['server-time'], - kindHint: 'fetch', + kind: IncrementalCacheKind.FETCH, }) expect(res?.value).toEqual({ @@ -101,7 +107,7 @@ describe('FileSystemCache (isrMemory 0)', () => { await fsCache.set( 'unstable-cache', { - kind: 'FETCH', + kind: CachedRouteKind.FETCH, data: { headers: {}, body: '1700056381', status: 200, url: '' }, revalidate: 30, }, @@ -110,7 +116,7 @@ describe('FileSystemCache (isrMemory 0)', () => { const res = await fsCache.get('unstable-cache', { tags: ['server-time'], - kindHint: 'fetch', + kind: IncrementalCacheKind.FETCH, }) expect(res?.value).toEqual({