File size: 3,800 Bytes
b91e262 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | import type { WorkStore } from '../app-render/work-async-storage.external'
import {
delayUntilRuntimeStage,
postponeWithTracking,
type DynamicTrackingState,
} from '../app-render/dynamic-rendering'
import {
throwInvariantForMissingStore,
workUnitAsyncStorage,
type StaticPrerenderStore,
} from '../app-render/work-unit-async-storage.external'
import { makeHangingPromise } from '../dynamic-rendering-utils'
import { InvariantError } from '../../shared/lib/invariant-error'
export function createServerPathnameForMetadata(
underlyingPathname: string,
workStore: WorkStore
): Promise<string> {
const workUnitStore = workUnitAsyncStorage.getStore()
if (workUnitStore) {
switch (workUnitStore.type) {
case 'prerender':
case 'prerender-client':
case 'prerender-ppr':
case 'prerender-legacy': {
return createPrerenderPathname(
underlyingPathname,
workStore,
workUnitStore
)
}
case 'cache':
case 'private-cache':
case 'unstable-cache':
throw new InvariantError(
'createServerPathnameForMetadata should not be called in cache contexts.'
)
case 'prerender-runtime':
return delayUntilRuntimeStage(
workUnitStore,
createRenderPathname(underlyingPathname)
)
case 'request':
return createRenderPathname(underlyingPathname)
default:
workUnitStore satisfies never
}
}
throwInvariantForMissingStore()
}
function createPrerenderPathname(
underlyingPathname: string,
workStore: WorkStore,
prerenderStore: StaticPrerenderStore
): Promise<string> {
switch (prerenderStore.type) {
case 'prerender-client':
throw new InvariantError(
'createPrerenderPathname was called inside a client component scope.'
)
case 'prerender': {
const fallbackParams = prerenderStore.fallbackRouteParams
if (fallbackParams && fallbackParams.size > 0) {
return makeHangingPromise<string>(
prerenderStore.renderSignal,
workStore.route,
'`pathname`'
)
}
break
}
case 'prerender-ppr': {
const fallbackParams = prerenderStore.fallbackRouteParams
if (fallbackParams && fallbackParams.size > 0) {
return makeErroringPathname(workStore, prerenderStore.dynamicTracking)
}
break
}
case 'prerender-legacy':
break
default:
prerenderStore satisfies never
}
// We don't have any fallback params so we have an entirely static safe params object
return Promise.resolve(underlyingPathname)
}
function makeErroringPathname<T>(
workStore: WorkStore,
dynamicTracking: null | DynamicTrackingState
): Promise<T> {
let reject: null | ((reason: unknown) => void) = null
const promise = new Promise<T>((_, re) => {
reject = re
})
const originalThen = promise.then.bind(promise)
// We instrument .then so that we can generate a tracking event only if you actually
// await this promise, not just that it is created.
promise.then = (onfulfilled, onrejected) => {
if (reject) {
try {
postponeWithTracking(
workStore.route,
'metadata relative url resolving',
dynamicTracking
)
} catch (error) {
reject(error)
reject = null
}
}
return originalThen(onfulfilled, onrejected)
}
// We wrap in a noop proxy to trick the runtime into thinking it
// isn't a native promise (it's not really). This is so that awaiting
// the promise will call the `then` property triggering the lazy postpone
return new Proxy(promise, {})
}
function createRenderPathname(underlyingPathname: string): Promise<string> {
return Promise.resolve(underlyingPathname)
}
|