Skip to content

Commit

Permalink
fix: ensure storage is accessed correctly, improved patch fetch typings
Browse files Browse the repository at this point in the history
  • Loading branch information
wyattjoh committed Apr 4, 2024
1 parent 5c29e93 commit 859af7b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 70 deletions.
16 changes: 6 additions & 10 deletions packages/next/src/client/components/error-boundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import React from 'react'
import { usePathname } from './navigation'
import { isNextRouterError } from './is-next-router-error'
import { staticGenerationAsyncStorage } from './static-generation-async-storage.external'

const styles = {
error: {
Expand Down Expand Up @@ -50,17 +51,12 @@ interface ErrorBoundaryHandlerState {
// function crashes so we can maintain our previous cache
// instead of caching the error page
function HandleISRError({ error }: { error: any }) {
if (typeof (fetch as any).__nextGetStaticStore === 'function') {
const store:
| undefined
| import('./static-generation-async-storage.external').StaticGenerationStore =
(fetch as any).__nextGetStaticStore()?.getStore()

if (store?.isRevalidate || store?.isStaticGeneration) {
console.error(error)
throw error
}
const store = staticGenerationAsyncStorage.getStore()
if (store?.isRevalidate || store?.isStaticGeneration) {
console.error(error)
throw error
}

return null
}

Expand Down
91 changes: 53 additions & 38 deletions packages/next/src/server/lib/patch-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ import type { FetchMetric } from '../base-http'

const isEdgeRuntime = process.env.NEXT_RUNTIME === 'edge'

type Fetcher = typeof fetch

type PatchedFetch = Fetcher & {
__nextPatched?: true
__nextGetStaticStore?: () => StaticGenerationAsyncStorage
_nextOriginalFetch?: Fetcher
}

function isPatchedFetch(fetch: Fetcher | PatchedFetch): fetch is PatchedFetch {
return '__nextPatched' in fetch && fetch.__nextPatched === true
}

export function validateRevalidate(
revalidateVal: unknown,
pathname: string
Expand Down Expand Up @@ -174,25 +186,14 @@ interface PatchableModule {
staticGenerationAsyncStorage: StaticGenerationAsyncStorage
}

// we patch fetch to collect cache information used for
// determining if a page is static or not
export function patchFetch({
serverHooks,
staticGenerationAsyncStorage,
}: PatchableModule) {
if (!(globalThis as any)._nextOriginalFetch) {
;(globalThis as any)._nextOriginalFetch = globalThis.fetch
}

if ((globalThis.fetch as any).__nextPatched) return

const { DynamicServerError } = serverHooks
const originFetch: typeof fetch = (globalThis as any)._nextOriginalFetch

globalThis.fetch = async (
input: RequestInfo | URL,
init: RequestInit | undefined
) => {
function createPatchedFetcher(
originFetch: Fetcher,
{
serverHooks: { DynamicServerError },
staticGenerationAsyncStorage,
}: PatchableModule
): Fetcher {
return async (input: RequestInfo | URL, init: RequestInit | undefined) => {
let url: URL | undefined
try {
url = new URL(input instanceof Request ? input.url : input)
Expand All @@ -211,7 +212,7 @@ export function patchFetch({
const isInternal = (init?.next as any)?.internal === true
const hideSpan = process.env.NEXT_OTEL_FETCH_DISABLED === '1'

return await getTracer().trace(
return getTracer().trace(
isInternal ? NextNodeServerSpan.internalFetch : AppRenderSpan.fetch,
{
hideSpan,
Expand All @@ -225,9 +226,18 @@ export function patchFetch({
},
},
async () => {
const staticGenerationStore: StaticGenerationStore =
staticGenerationAsyncStorage.getStore() ||
(fetch as any).__nextGetStaticStore?.()
// If this is an internal fetch, we should not do any special treatment.
if (isInternal) return originFetch(input, init)

const staticGenerationStore = staticGenerationAsyncStorage.getStore()

// If the staticGenerationStore is not available, we can't do any
// special treatment of fetch, therefore fallback to the original
// fetch implementation.
if (!staticGenerationStore || staticGenerationStore.isDraftMode) {
return originFetch(input, init)
}

const isRequestInput =
input &&
typeof input === 'object' &&
Expand All @@ -239,17 +249,6 @@ export function patchFetch({
return value || (isRequestInput ? (input as any)[field] : null)
}

// If the staticGenerationStore is not available, we can't do any
// special treatment of fetch, therefore fallback to the original
// fetch implementation.
if (
!staticGenerationStore ||
isInternal ||
staticGenerationStore.isDraftMode
) {
return originFetch(input, init)
}

let revalidate: number | undefined | false = undefined
const getNextField = (field: 'revalidate' | 'tags') => {
return typeof init?.next?.[field] !== 'undefined'
Expand Down Expand Up @@ -718,8 +717,24 @@ export function patchFetch({
}
)
}
;(globalThis.fetch as any).__nextGetStaticStore = () => {
return staticGenerationAsyncStorage
}
;(globalThis.fetch as any).__nextPatched = true
}

// we patch fetch to collect cache information used for
// determining if a page is static or not
export function patchFetch(options: PatchableModule) {
// If we've already patched fetch, we should not patch it again.
if (isPatchedFetch(globalThis.fetch)) return

// Grab the original fetch function. We'll attach this so we can use it in
// the patched fetch function.
const original = globalThis.fetch

// Assign the patched fetch.
globalThis.fetch = createPatchedFetcher(original, options)

// Mark the fetch function as patched.
const patched = globalThis.fetch as PatchedFetch
patched.__nextPatched = true
patched.__nextGetStaticStore = () => options.staticGenerationAsyncStorage
patched._nextOriginalFetch = original
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { RequestCookies } from '../cookies'
import type { StaticGenerationStore } from '../../../../client/components/static-generation-async-storage.external'

import { ResponseCookies } from '../cookies'
import { ReflectAdapter } from './reflect'
import { staticGenerationAsyncStorage } from '../../../../client/components/static-generation-async-storage.external'

/**
* @internal
Expand Down Expand Up @@ -106,9 +106,7 @@ export class MutableRequestCookiesAdapter {
const modifiedCookies = new Set<string>()
const updateResponseCookies = () => {
// TODO-APP: change method of getting staticGenerationAsyncStore
const staticGenerationAsyncStore = (fetch as any)
.__nextGetStaticStore?.()
?.getStore() as undefined | StaticGenerationStore
const staticGenerationAsyncStore = staticGenerationAsyncStorage.getStore()
if (staticGenerationAsyncStore) {
staticGenerationAsyncStore.pathWasRevalidated = true
}
Expand Down
13 changes: 2 additions & 11 deletions packages/next/src/server/web/spec-extension/revalidate.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import type {
StaticGenerationAsyncStorage,
StaticGenerationStore,
} from '../../../client/components/static-generation-async-storage.external'
import { trackDynamicDataAccessed } from '../../app-render/dynamic-rendering'
import { isDynamicRoute } from '../../../shared/lib/router/utils'
import {
NEXT_CACHE_IMPLICIT_TAG_ID,
NEXT_CACHE_SOFT_TAG_MAX_LENGTH,
} from '../../../lib/constants'
import { getPathname } from '../../../lib/url'
import { staticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage.external'

/**
* This function allows you to purge [cached data](https://nextjs.org/docs/app/building-your-application/caching) on-demand for a specific cache tag.
Expand Down Expand Up @@ -45,13 +42,7 @@ export function revalidatePath(originalPath: string, type?: 'layout' | 'page') {
}

function revalidate(tag: string, expression: string) {
const staticGenerationAsyncStorage = (
fetch as any
).__nextGetStaticStore?.() as undefined | StaticGenerationAsyncStorage

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

const store = staticGenerationAsyncStorage.getStore()
if (!store || !store.incrementalCache) {
throw new Error(
`Invariant: static generation store missing in ${expression}`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type { StaticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage.external'
import type { IncrementalCache } from '../../lib/incremental-cache'

import { staticGenerationAsyncStorage as _staticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage.external'
import { CACHE_ONE_YEAR } from '../../../lib/constants'
import {
addImplicitTags,
validateRevalidate,
validateTags,
} from '../../lib/patch-fetch'
import { staticGenerationAsyncStorage } from '../../../client/components/static-generation-async-storage.external'

type Callback = (...args: any[]) => Promise<any>

Expand Down Expand Up @@ -59,11 +58,6 @@ export function unstable_cache<T extends Callback>(
tags?: string[]
} = {}
): T {
const staticGenerationAsyncStorage =
((fetch as any).__nextGetStaticStore?.() as
| StaticGenerationAsyncStorage
| undefined) ?? _staticGenerationAsyncStorage

if (options.revalidate === 0) {
throw new Error(
`Invariant revalidate: 0 can not be passed to unstable_cache(), must be "false" or "> 0" ${cb.toString()}`
Expand Down

0 comments on commit 859af7b

Please sign in to comment.