rsnext/packages/next/server/lib/incremental-cache/file-system-cache.ts
JJ Kasper 15354e9b42
Migrate prerender lru cache into fs cache handler (#37326)
Continuation of https://github.com/vercel/next.js/pull/37258 this moves the LRU cache to the `file-system-cache` so that any different cache handlers don't need to disable the LRU cache as this may cause inconsistencies with some cache handlers that need to be the source of truth.
2022-05-31 07:05:41 +00:00

83 lines
2.4 KiB
TypeScript

import LRUCache from 'next/dist/compiled/lru-cache'
import path from '../../../shared/lib/isomorphic/path'
import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './'
export default class FileSystemCache implements CacheHandler {
private fs: CacheHandlerContext['fs']
private flushToDisk?: CacheHandlerContext['flushToDisk']
private serverDistDir: CacheHandlerContext['serverDistDir']
private memoryCache?: LRUCache<string, CacheHandlerValue>
constructor(ctx: CacheHandlerContext) {
this.fs = ctx.fs
this.flushToDisk = ctx.flushToDisk
this.serverDistDir = ctx.serverDistDir
if (ctx.maxMemoryCacheSize) {
this.memoryCache = new LRUCache({
max: ctx.maxMemoryCacheSize,
length({ value }) {
if (!value) {
return 25
} else if (value.kind === 'REDIRECT') {
return JSON.stringify(value.props).length
} else if (value.kind === 'IMAGE') {
throw new Error('invariant image should not be incremental-cache')
}
// rough estimate of size of cache value
return value.html.length + JSON.stringify(value.pageData).length
},
})
}
}
public async get(key: string) {
let data = this.memoryCache?.get(key)
// let's check the disk for seed data
if (!data) {
try {
const htmlPath = this.getFsPath(`${key}.html`)
const html = await this.fs.readFile(htmlPath)
const pageData = JSON.parse(
await this.fs.readFile(this.getFsPath(`${key}.json`))
)
const { mtime } = await this.fs.stat(htmlPath)
data = {
lastModified: mtime.getTime(),
value: {
kind: 'PAGE',
html,
pageData,
},
}
this.memoryCache?.set(key, data)
} catch (_) {
// unable to get data from disk
}
}
return data || null
}
public async set(key: string, data: CacheHandlerValue['value']) {
if (!this.flushToDisk) return
this.memoryCache?.set(key, {
value: data,
lastModified: Date.now(),
})
if (data?.kind === 'PAGE') {
await this.fs.writeFile(this.getFsPath(`${key}.html`), data.html)
await this.fs.writeFile(
this.getFsPath(`${key}.json`),
JSON.stringify(data.pageData)
)
}
}
private getFsPath(pathname: string): string {
return path.join(this.serverDistDir, 'pages', pathname)
}
}