|
| 1 | +import { staticGenerationAsyncStorage } from '../../client/components/static-generation-async-storage.external' |
| 2 | +import { prerenderAsyncStorage } from '../app-render/prerender-async-storage.external' |
| 3 | +import { |
| 4 | + postponeWithTracking, |
| 5 | + interruptStaticGeneration, |
| 6 | + trackDynamicDataInDynamicRender, |
| 7 | +} from '../app-render/dynamic-rendering' |
| 8 | +import { StaticGenBailoutError } from '../../client/components/static-generation-bailout' |
| 9 | + |
| 10 | +/** |
| 11 | + * This function allows you to indicate that you require an actual user Request before continuing. |
| 12 | + * |
| 13 | + * During prerendering it will never resolve and during rendering it resolves immediately. |
| 14 | + */ |
| 15 | +export function connection(): Promise<void> { |
| 16 | + const staticGenerationStore = staticGenerationAsyncStorage.getStore() |
| 17 | + const prerenderStore = prerenderAsyncStorage.getStore() |
| 18 | + |
| 19 | + if (staticGenerationStore) { |
| 20 | + if (staticGenerationStore.forceStatic) { |
| 21 | + // When using forceStatic we override all other logic and always just return an empty |
| 22 | + // headers object without tracking |
| 23 | + return Promise.resolve(undefined) |
| 24 | + } |
| 25 | + |
| 26 | + if (staticGenerationStore.isUnstableCacheCallback) { |
| 27 | + throw new Error( |
| 28 | + `Route ${staticGenerationStore.route} used "connection" inside a function cached with "unstable_cache(...)". The \`connection()\` function is used to wait indicate the subsequent code must only run when there is an actual Request but caches must be able to be produced before a Request so this function is not allowed in this scope. See more info here: https://nextjs.org/docs/app/api-reference/functions/unstable_cache` |
| 29 | + ) |
| 30 | + } else if (staticGenerationStore.dynamicShouldError) { |
| 31 | + throw new StaticGenBailoutError( |
| 32 | + `Route ${staticGenerationStore.route} with \`dynamic = "error"\` couldn't be rendered statically because it used \`connection\`. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering` |
| 33 | + ) |
| 34 | + } |
| 35 | + |
| 36 | + if (prerenderStore) { |
| 37 | + // We are in PPR and/or dynamicIO mode and prerendering |
| 38 | + |
| 39 | + if (prerenderStore.controller || prerenderStore.cacheSignal) { |
| 40 | + // We use the controller and cacheSignal as an indication we are in dynamicIO mode. |
| 41 | + // When resolving headers for a prerender with dynamic IO we return a forever promise |
| 42 | + // along with property access tracked synchronous headers. |
| 43 | + |
| 44 | + // We don't track dynamic access here because access will be tracked when you access |
| 45 | + // one of the properties of the headers object. |
| 46 | + return new Promise(hangForever) |
| 47 | + } else { |
| 48 | + // We are prerendering with PPR. We need track dynamic access here eagerly |
| 49 | + // to keep continuity with how headers has worked in PPR without dynamicIO. |
| 50 | + // TODO consider switching the semantic to throw on property access intead |
| 51 | + postponeWithTracking( |
| 52 | + staticGenerationStore.route, |
| 53 | + 'connection', |
| 54 | + prerenderStore.dynamicTracking |
| 55 | + ) |
| 56 | + } |
| 57 | + } else if (staticGenerationStore.isStaticGeneration) { |
| 58 | + // We are in a legacy static generation mode while prerendering |
| 59 | + // We treat this function call as a bailout of static generation |
| 60 | + interruptStaticGeneration('connection', staticGenerationStore) |
| 61 | + } |
| 62 | + // We fall through to the dynamic context below but we still track dynamic access |
| 63 | + // because in dev we can still error for things like using headers inside a cache context |
| 64 | + trackDynamicDataInDynamicRender(staticGenerationStore) |
| 65 | + } |
| 66 | + |
| 67 | + return Promise.resolve(undefined) |
| 68 | +} |
| 69 | + |
| 70 | +function hangForever() {} |
0 commit comments