diff --git a/packages/next/src/export/worker.ts b/packages/next/src/export/worker.ts index 5fe8e690ff295..58e77fa74b461 100644 --- a/packages/next/src/export/worker.ts +++ b/packages/next/src/export/worker.ts @@ -371,7 +371,9 @@ export default async function exportPage({ readFileSync: (f) => fs.readFileSync(f), writeFile: (f, d) => fs.promises.writeFile(f, d), mkdir: (dir) => fs.promises.mkdir(dir, { recursive: true }), + mkdirSync: (dir) => fs.mkdirSync(dir, { recursive: true }), stat: (f) => fs.promises.stat(f), + createWriteStream: (f) => fs.createWriteStream(f), }, serverDistDir: join(distDir, 'server'), CurCacheHandler: CacheHandler, 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 cc8c5c12fdf86..3f9495e1346ff 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,10 +1,11 @@ import type { OutgoingHttpHeaders } from 'http' -import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './' import LRUCache from 'next/dist/compiled/lru-cache' -import { CacheFs } from '../../../shared/lib/utils' + import path from '../../../shared/lib/isomorphic/path' +import { CacheFs } from '../../../shared/lib/utils' import { CachedFetchValue } from '../../response-cache' import { getDerivedTags } from './utils' +import type { CacheHandler, CacheHandlerContext, CacheHandlerValue } from './' type FileSystemCacheContext = Omit< CacheHandlerContext, @@ -59,11 +60,15 @@ export default class FileSystemCache implements CacheHandler { }) } if (this.serverDistDir && this.fs) { - this.tagsManifestPath = path.join( + const fetchCacheDirectory = path.join( this.serverDistDir, '..', 'cache', - 'fetch-cache', + 'fetch-cache' + ) + this.fs.mkdirSync(fetchCacheDirectory) + this.tagsManifestPath = path.join( + fetchCacheDirectory, 'tags-manifest.json' ) this.loadTagsManifest() @@ -107,27 +112,32 @@ export default class FileSystemCache implements CacheHandler { } public async revalidateTag(tag: string) { - // we need to ensure the tagsManifest is refreshed - // since separate workers can be updating it at the same - // time and we can't flush out of sync data - this.loadTagsManifest() - if (!tagsManifest || !this.tagsManifestPath) { + if (!this.tagsManifestPath) { return } - const data = tagsManifest.items[tag] || { keys: [] } - data.revalidatedAt = Date.now() - tagsManifest.items[tag] = data + const manifestWriteStream = this.fs.createWriteStream(this.tagsManifestPath) - try { - await this.fs.mkdir(path.dirname(this.tagsManifestPath)) - await this.fs.writeFile( - this.tagsManifestPath, - JSON.stringify(tagsManifest || {}) - ) - } catch (err: any) { - console.warn('Failed to update tags manifest.', err) - } + return new Promise((resolve) => { + manifestWriteStream.on('ready', () => { + this.loadTagsManifest() + if (!tagsManifest) { + return + } + const data = tagsManifest.items[tag] || { + keys: [], + } + data.revalidatedAt = Date.now() + tagsManifest.items[tag] = data + manifestWriteStream.write(JSON.stringify(tagsManifest)) + manifestWriteStream.end() + }) + manifestWriteStream.on('finish', resolve) + manifestWriteStream.on('error', (err) => { + console.warn('Failed to update tags manifest.', err) + resolve() + }) + }) } public async get(key: string, fetchCache?: boolean) { diff --git a/packages/next/src/server/lib/node-fs-methods.ts b/packages/next/src/server/lib/node-fs-methods.ts index 490c4e0336c4a..5007bed4c715a 100644 --- a/packages/next/src/server/lib/node-fs-methods.ts +++ b/packages/next/src/server/lib/node-fs-methods.ts @@ -6,5 +6,7 @@ export const nodeFs: CacheFs = { readFileSync: (f) => _fs.readFileSync(f), writeFile: (f, d) => _fs.promises.writeFile(f, d), mkdir: (dir) => _fs.promises.mkdir(dir, { recursive: true }), + mkdirSync: (dir) => _fs.mkdirSync(dir, { recursive: true }), stat: (f) => _fs.promises.stat(f), + createWriteStream: (f) => _fs.createWriteStream(f), } diff --git a/packages/next/src/shared/lib/utils.ts b/packages/next/src/shared/lib/utils.ts index f967be459022b..b00c7337f0594 100644 --- a/packages/next/src/shared/lib/utils.ts +++ b/packages/next/src/shared/lib/utils.ts @@ -1,12 +1,14 @@ -import type { HtmlProps } from './html-context' -import type { ComponentType } from 'react' -import type { DomainLocale } from '../../server/config' import type { Env } from '@next/env' +import type { WriteStream } from 'fs' import type { IncomingMessage, ServerResponse } from 'http' -import type { NextRouter } from './router/router' -import type { ParsedUrlQuery } from 'querystring' import type { PreviewData } from 'next/types' +import type { ParsedUrlQuery } from 'querystring' +import type { ComponentType } from 'react' + +import type { DomainLocale } from '../../server/config' import { COMPILER_NAMES } from './constants' +import type { HtmlProps } from './html-context' +import type { NextRouter } from './router/router' export type NextComponentType< Context extends BaseContext = NextPageContext, @@ -450,7 +452,9 @@ export interface CacheFs { readFileSync(f: string): Buffer writeFile(f: string, d: any): Promise mkdir(dir: string): Promise + mkdirSync(dir: string): string | undefined stat(f: string): Promise<{ mtime: Date }> + createWriteStream(f: string): WriteStream } export function stringifyError(error: Error) {