Skip to content

Commit

Permalink
Tweak app cache handling for paths (#49108)
Browse files Browse the repository at this point in the history
This updates some handling as discussed and cleans up un-necessary
fields from previous PR.

---------
  • Loading branch information
ijjk authored May 3, 2023
1 parent 3cb15a0 commit 5cae80d
Show file tree
Hide file tree
Showing 13 changed files with 38 additions and 120 deletions.
1 change: 1 addition & 0 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,7 @@ export async function buildAppStaticPaths({
{
pathname: page,
renderOpts: {
originalPathname: page,
incrementalCache,
supportsDynamicHTML: true,
isRevalidate: false,
Expand Down
5 changes: 5 additions & 0 deletions packages/next/src/build/webpack/loaders/next-app-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,16 @@ async function createAppRouteCode({
staticGenerationBailout
} = routeModule
const originalPathname = "${page}"
export {
routeModule,
requestAsyncStorage,
staticGenerationAsyncStorage,
serverHooks,
headerHooks,
staticGenerationBailout,
originalPathname
}`
}

Expand Down Expand Up @@ -595,6 +598,8 @@ const nextAppLoader: AppLoader = async function nextAppLoader() {
export { renderToReadableStream, decodeReply } from 'react-server-dom-webpack/server.edge'
export const __next_app_webpack_require__ = __webpack_require__
export { preloadStyle, preloadFont, preconnect } from 'next/dist/server/app-render/rsc/preloads'
export const originalPathname = "${page}"
`

return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { createAsyncLocalStorage } from './async-local-storage'
export interface StaticGenerationStore {
readonly isStaticGeneration: boolean
readonly pathname: string
readonly originalPathname?: string
readonly incrementalCache?: IncrementalCache
readonly isRevalidate?: boolean
readonly isMinimalMode?: boolean
readonly isOnDemandRevalidate?: boolean
readonly isPrerendering?: boolean

Expand Down
5 changes: 3 additions & 2 deletions packages/next/src/export/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ interface RenderOpts {
supportsDynamicHTML?: boolean
incrementalCache?: IncrementalCache
strictNextHead?: boolean
originalPathname?: string
}

export default async function exportPage({
Expand Down Expand Up @@ -323,9 +324,9 @@ export default async function exportPage({
fontManifest: optimizeFonts ? requireFontManifest(distDir) : null,
locale: locale as string,
supportsDynamicHTML: false,
originalPathname: page,
...(ciEnvironment.hasNextSupport
? {
isMinimalMode: true,
isRevalidate: true,
}
: {}),
Expand Down Expand Up @@ -404,12 +405,12 @@ export default async function exportPage({
notFoundRoutes: [],
},
staticGenerationContext: {
originalPathname: page,
nextExport: true,
supportsDynamicHTML: false,
incrementalCache: curRenderOpts.incrementalCache,
...(ciEnvironment.hasNextSupport
? {
isMinimalMode: true,
isRevalidate: true,
}
: {}),
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/app-render/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export type RenderOptsPartial = {
nextExport?: boolean
nextConfigOutput?: 'standalone' | 'export'
appDirDevErrorLogger?: (err: any) => Promise<void>
originalPathname?: string
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { IncrementalCache } from '../lib/incremental-cache'
export type StaticGenerationContext = {
pathname: string
renderOpts: {
originalPathname?: string
incrementalCache?: IncrementalCache
supportsDynamicHTML: boolean
isRevalidate?: boolean
Expand Down Expand Up @@ -54,6 +55,7 @@ export const StaticGenerationAsyncStorageWrapper: AsyncStorageWrapper<
const store: StaticGenerationStore = {
isStaticGeneration,
pathname,
originalPathname: renderOpts.originalPathname,
incrementalCache:
// we fallback to a global incremental cache for edge-runtime locally
// so that it can access the fs cache without mocks
Expand Down
3 changes: 2 additions & 1 deletion packages/next/src/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
params: match.params,
prerenderManifest: this.getPrerenderManifest(),
staticGenerationContext: {
originalPathname: components.ComponentMod.originalPathname,
supportsDynamicHTML,
incrementalCache,
isRevalidate: isSSG,
Expand Down Expand Up @@ -1703,6 +1704,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
? {
incrementalCache,
isRevalidate: isSSG,
originalPathname: components.ComponentMod.originalPathname,
}
: {}),
isDataReq,
Expand All @@ -1725,7 +1727,6 @@ export default abstract class Server<ServerOptions extends Options = Options> {

supportsDynamicHTML,
isOnDemandRevalidate,
isMinimalMode: this.minimalMode,
}

const renderResult = await this.renderHTML(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export default class FileSystemCache implements CacheHandler {
}

const getDerivedTags = (tags: string[]): string[] => {
const derivedTags: string[] = []
const derivedTags: string[] = ['/']

for (const tag of tags || []) {
if (tag.startsWith('/')) {
Expand Down
19 changes: 15 additions & 4 deletions packages/next/src/server/lib/patch-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'
export function addImplicitTags(
staticGenerationStore: ReturnType<StaticGenerationAsyncStorage['getStore']>
) {
if (!staticGenerationStore?.pathname) return
const newTags: string[] = []
const pathname = staticGenerationStore?.originalPathname
if (!pathname) {
return newTags
}

if (!Array.isArray(staticGenerationStore.tags)) {
staticGenerationStore.tags = []
}
if (!staticGenerationStore.tags.includes(staticGenerationStore.pathname)) {
staticGenerationStore.tags.push(staticGenerationStore.pathname)
if (!staticGenerationStore.tags.includes(pathname)) {
staticGenerationStore.tags.push(pathname)
}
newTags.push(pathname)
return newTags
}

// we patch fetch to collect cache information used for
Expand Down Expand Up @@ -106,8 +112,13 @@ export function patchFetch({
}
}
}
addImplicitTags(staticGenerationStore)
const implicitTags = addImplicitTags(staticGenerationStore)

for (const tag of implicitTags || []) {
if (!tags.includes(tag)) {
tags.push(tag)
}
}
const isOnlyCache = staticGenerationStore.fetchCache === 'only-cache'
const isForceCache = staticGenerationStore.fetchCache === 'force-cache'
const isDefaultNoStore =
Expand Down
1 change: 0 additions & 1 deletion packages/next/src/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ export type RenderOptsPartial = {
largePageDataBytes?: number
isOnDemandRevalidate?: boolean
strictNextHead: boolean
isMinimalMode?: boolean
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
Expand Down
Original file line number Diff line number Diff line change
@@ -1,108 +1,5 @@
import type {
StaticGenerationAsyncStorage,
StaticGenerationStore,
} from '../../../client/components/static-generation-async-storage'

import { unstable_revalidateTag } from './unstable-revalidate-tag'
import { headers } from '../../../client/components/headers'
import {
PRERENDER_REVALIDATE_HEADER,
PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER,
} from '../../../lib/constants'

export function unstable_revalidatePath(
path: string,
ctx: {
manualRevalidate?: boolean
unstable_onlyGenerated?: boolean
} = {}
) {
if (!ctx?.manualRevalidate) {
return unstable_revalidateTag(path)
}

const staticGenerationAsyncStorage = (
fetch as any
).__nextGetStaticStore?.() as undefined | StaticGenerationAsyncStorage

const store: undefined | StaticGenerationStore =
staticGenerationAsyncStorage?.getStore()

if (!store) {
throw new Error(
`Invariant: static generation store missing in unstable_revalidatePath ${path}`
)
}

if (!store.pendingRevalidates) {
store.pendingRevalidates = []
}
const previewModeId =
store.incrementalCache?.prerenderManifest?.preview?.previewModeId ||
process.env.__NEXT_PREVIEW_MODE_ID

const reqHeaders: Record<string, undefined | string | string[]> =
store.incrementalCache?.requestHeaders || Object.fromEntries(headers())

const host = reqHeaders['host']
const proto = store.incrementalCache?.requestProtocol || 'https'

// TODO: glob handling + blocking/soft revalidate
const revalidateURL = `${proto}://${host}${path}`

const revalidateHeaders: typeof reqHeaders = {
[PRERENDER_REVALIDATE_HEADER]: previewModeId,
...(ctx.unstable_onlyGenerated
? {
[PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER]: '1',
}
: {}),
}

const curAllowedRevalidateHeaderKeys =
store.incrementalCache?.allowedRevalidateHeaderKeys ||
process.env.__NEXT_ALLOWED_REVALIDATE_HEADERS

const allowedRevalidateHeaderKeys = [
...(curAllowedRevalidateHeaderKeys || []),
...(!store.incrementalCache
? ['cookie', 'x-vercel-protection-bypass']
: []),
]

for (const key of Object.keys(reqHeaders)) {
if (allowedRevalidateHeaderKeys.includes(key)) {
revalidateHeaders[key] = reqHeaders[key] as string
}
}

const fetchIPv4v6 = (v6 = false): Promise<any> => {
const curUrl = new URL(revalidateURL)
const hostname = curUrl.hostname

if (!v6 && hostname === 'localhost') {
curUrl.hostname = '127.0.0.1'
}
return fetch(curUrl, {
method: 'HEAD',
headers: revalidateHeaders as HeadersInit,
})
.then((res) => {
const cacheHeader =
res.headers.get('x-vercel-cache') || res.headers.get('x-nextjs-cache')
if (cacheHeader?.toLowerCase() !== 'revalidated') {
throw new Error(
`received invalid response ${res.status} ${cacheHeader}`
)
}
})
.catch((err) => {
if (err.code === 'ECONNREFUSED' && !v6) {
return fetchIPv4v6(true)
}
console.error(`revalidatePath failed for ${revalidateURL}`, err)
})
}

store.pendingRevalidates.push(fetchIPv4v6())
export function unstable_revalidatePath(path: string) {
return unstable_revalidateTag(path)
}
2 changes: 1 addition & 1 deletion test/e2e/app-dir/app-static/app-static.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ createNextDescribe(
initialHeaders: {
'content-type': 'application/json',
'x-next-cache-tags':
'thankyounext,/route-handler/revalidate-360-isr',
'thankyounext,/route-handler/revalidate-360-isr/route',
},
initialRevalidateSeconds: false,
srcRoute: '/route-handler/revalidate-360-isr',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ describe('should set-up next', () => {

it('should send cache tags in minimal mode for ISR', async () => {
for (const [path, tags] of [
['/isr/first', 'isr-page,/isr/[slug]'],
['/isr/second', 'isr-page,/isr/[slug]'],
['/api/isr/first', 'isr-page,/api/isr/[slug]'],
['/api/isr/second', 'isr-page,/api/isr/[slug]'],
['/isr/first', 'isr-page,/isr/[slug]/page'],
['/isr/second', 'isr-page,/isr/[slug]/page'],
['/api/isr/first', 'isr-page,/api/isr/[slug]/route'],
['/api/isr/second', 'isr-page,/api/isr/[slug]/route'],
]) {
const res = await fetchViaHTTP(appPort, path, undefined, {
redirect: 'manual',
Expand Down

0 comments on commit 5cae80d

Please sign in to comment.