AbdulElahGwaith's picture
Upload folder using huggingface_hub
b91e262 verified
import { workAsyncStorage } from '../app-render/work-async-storage.external'
import { workUnitAsyncStorage } from '../app-render/work-unit-async-storage.external'
import { abortOnSynchronousPlatformIOAccess } from '../app-render/dynamic-rendering'
import { InvariantError } from '../../shared/lib/invariant-error'
import { RenderStage } from '../app-render/staged-rendering'
import { getServerReact, getClientReact } from '../runtime-reacts.external'
type ApiType = 'time' | 'random' | 'crypto'
export function io(expression: string, type: ApiType) {
const workUnitStore = workUnitAsyncStorage.getStore()
const workStore = workAsyncStorage.getStore()
if (!workUnitStore || !workStore) {
return
}
switch (workUnitStore.type) {
case 'prerender':
case 'prerender-runtime': {
const prerenderSignal = workUnitStore.controller.signal
if (prerenderSignal.aborted === false) {
// If the prerender signal is already aborted we don't need to construct
// any stacks because something else actually terminated the prerender.
let message: string
switch (type) {
case 'time':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing the current time in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time`
break
case 'random':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-random`
break
case 'crypto':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random cryptographic values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-crypto`
break
default:
throw new InvariantError(
'Unknown expression type in abortOnSynchronousPlatformIOAccess.'
)
}
abortOnSynchronousPlatformIOAccess(
workStore.route,
expression,
applyOwnerStack(new Error(message)),
workUnitStore
)
}
break
}
case 'prerender-client': {
const prerenderSignal = workUnitStore.controller.signal
if (prerenderSignal.aborted === false) {
// If the prerender signal is already aborted we don't need to construct
// any stacks because something else actually terminated the prerender.
let message: string
switch (type) {
case 'time':
message = `Route "${workStore.route}" used ${expression} inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time-client`
break
case 'random':
message = `Route "${workStore.route}" used ${expression} inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-random-client`
break
case 'crypto':
message = `Route "${workStore.route}" used ${expression} inside a Client Component without a Suspense boundary above it. See more info here: https://nextjs.org/docs/messages/next-prerender-crypto-client`
break
default:
throw new InvariantError(
'Unknown expression type in abortOnSynchronousPlatformIOAccess.'
)
}
abortOnSynchronousPlatformIOAccess(
workStore.route,
expression,
applyOwnerStack(new Error(message)),
workUnitStore
)
}
break
}
case 'request':
if (process.env.NODE_ENV === 'development') {
const stageController = workUnitStore.stagedRendering
if (stageController && stageController.canSyncInterrupt()) {
let message: string
if (stageController.currentStage === RenderStage.Static) {
switch (type) {
case 'time':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing the current time in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-current-time`
break
case 'random':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-random`
break
case 'crypto':
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or Request data (e.g. \`cookies()\`, \`headers()\`, \`connection()\`, and \`searchParams\`). Accessing random cryptographic values synchronously in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: https://nextjs.org/docs/messages/next-prerender-crypto`
break
default:
throw new InvariantError(
'Unknown expression type in abortOnSynchronousPlatformIOAccess.'
)
}
} else {
// We're in the Runtime stage.
// We only error for Sync IO in the Runtime stage if the route has a runtime prefetch config.
// This check is implemented in `stageController.canSyncInterrupt()` --
// if runtime prefetching isn't enabled, then we won't get here.
let accessStatement: string
let additionalInfoLink: string
switch (type) {
case 'time':
accessStatement = 'the current time'
additionalInfoLink =
'https://nextjs.org/docs/messages/next-prerender-runtime-current-time'
break
case 'random':
accessStatement = 'random values synchronously'
additionalInfoLink =
'https://nextjs.org/docs/messages/next-prerender-runtime-random'
break
case 'crypto':
accessStatement = 'random cryptographic values synchronously'
additionalInfoLink =
'https://nextjs.org/docs/messages/next-prerender-runtime-crypto'
break
default:
throw new InvariantError(
'Unknown expression type in abortOnSynchronousPlatformIOAccess.'
)
}
message = `Route "${workStore.route}" used ${expression} before accessing either uncached data (e.g. \`fetch()\`) or awaiting \`connection()\`. When configured for Runtime prefetching, accessing ${accessStatement} in a Server Component requires reading one of these data sources first. Alternatively, consider moving this expression into a Client Component or Cache Component. See more info here: ${additionalInfoLink}`
}
const syncIOError = applyOwnerStack(new Error(message))
stageController.syncInterruptCurrentStageWithReason(syncIOError)
}
}
break
case 'prerender-ppr':
case 'prerender-legacy':
case 'cache':
case 'private-cache':
case 'unstable-cache':
break
default:
workUnitStore satisfies never
}
}
function applyOwnerStack(error: Error) {
// TODO: Instead of stitching the stacks here, we should log the original
// error as-is when it occurs, and let `patchErrorInspect` handle adding the
// owner stack, instead of logging it deferred in the `LogSafely` component
// via `throwIfDisallowedDynamic`.
if (process.env.NODE_ENV !== 'production') {
const ownerStack =
getClientReact()?.captureOwnerStack?.() ??
getServerReact()?.captureOwnerStack?.()
if (ownerStack) {
let stack = ownerStack
if (error.stack) {
const frames: string[] = []
for (const frame of error.stack.split('\n').slice(1)) {
if (frame.includes('react_stack_bottom_frame')) {
break
}
frames.push(frame)
}
stack = '\n' + frames.join('\n') + stack
}
error.stack = error.name + ': ' + error.message + stack
}
}
return error
}