From e2ae5e9df961989941d9c7f9752e1bee19b2e2aa Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 25 Sep 2024 13:48:00 -0700 Subject: [PATCH] fix: updated typescript types for React API's (#70410) This adds and updates some Typescript types for the React libraries used by Next.js. As a result some of the code was modified to match. --- .../src/server/app-render/action-handler.ts | 32 ++++++--- .../next/src/server/app-render/app-render.tsx | 32 +++------ .../app-render/create-error-handler.tsx | 11 ++- .../stream-utils/node-web-streams-helper.ts | 2 +- packages/next/types/$$compiled.internal.d.ts | 71 ++++++++++++++++++- packages/next/types/react-dom.d.ts | 6 +- 6 files changed, 115 insertions(+), 39 deletions(-) diff --git a/packages/next/src/server/app-render/action-handler.ts b/packages/next/src/server/app-render/action-handler.ts index 901d76f23b0db..b2e4f86df9c6e 100644 --- a/packages/next/src/server/app-render/action-handler.ts +++ b/packages/next/src/server/app-render/action-handler.ts @@ -567,7 +567,8 @@ export async function handleAction({ 'Cache-Control', 'no-cache, no-store, max-age=0, must-revalidate' ) - let bound = [] + + let boundActionArguments: unknown[] = [] const { actionAsyncStorage } = ComponentMod @@ -620,14 +621,18 @@ export async function handleAction({ // TODO-APP: Add streaming support const formData = await req.request.formData() if (isFetchAction) { - bound = await decodeReply(formData, serverModuleMap) + boundActionArguments = await decodeReply(formData, serverModuleMap) } else { const action = await decodeAction(formData, serverModuleMap) if (typeof action === 'function') { // Only warn if it's a server action, otherwise skip for other post requests warnBadServerActionRequest() const actionReturnedState = await action() - formState = decodeFormState(actionReturnedState, formData) + formState = decodeFormState( + actionReturnedState, + formData, + serverModuleMap + ) } // Skip the fetch path @@ -659,9 +664,12 @@ export async function handleAction({ if (isURLEncodedAction) { const formData = formDataFromSearchQueryString(actionData) - bound = await decodeReply(formData, serverModuleMap) + boundActionArguments = await decodeReply(formData, serverModuleMap) } else { - bound = await decodeReply(actionData, serverModuleMap) + boundActionArguments = await decodeReply( + actionData, + serverModuleMap + ) } } } else if ( @@ -723,7 +731,10 @@ export async function handleAction({ body.pipe(busboy) - bound = await decodeReplyFromBusboy(busboy, serverModuleMap) + boundActionArguments = await decodeReplyFromBusboy( + busboy, + serverModuleMap + ) } else { // React doesn't yet publish a busboy version of decodeAction // so we polyfill the parsing of FormData. @@ -779,9 +790,12 @@ export async function handleAction({ if (isURLEncodedAction) { const formData = formDataFromSearchQueryString(actionData) - bound = await decodeReply(formData, serverModuleMap) + boundActionArguments = await decodeReply(formData, serverModuleMap) } else { - bound = await decodeReply(actionData, serverModuleMap) + boundActionArguments = await decodeReply( + actionData, + serverModuleMap + ) } } } else { @@ -819,7 +833,7 @@ export async function handleAction({ actionId! ] - const returnVal = await actionHandler.apply(null, bound) + const returnVal = await actionHandler.apply(null, boundActionArguments) // For form actions, we need to continue rendering the page. if (isFetchAction) { diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx index 124bd74dc78e1..421e6356a6b06 100644 --- a/packages/next/src/server/app-render/app-render.tsx +++ b/packages/next/src/server/app-render/app-render.tsx @@ -22,7 +22,7 @@ import type { DeepReadonly } from '../../shared/lib/deep-readonly' import type { BaseNextRequest, BaseNextResponse } from '../base-http' import type { IncomingHttpHeaders } from 'http' -import React, { type ErrorInfo, type JSX } from 'react' +import React, { type JSX } from 'react' import RenderResult, { type AppPageRenderResultMetadata, @@ -506,7 +506,6 @@ async function generateDynamicFlightRenderResult( ctx.clientReferenceManifest.clientModules, { onError, - nonce: ctx.nonce, } ) @@ -1388,7 +1387,6 @@ async function renderToStream( clientReferenceManifest.clientModules, { onError: serverComponentsErrorHandler, - nonce: ctx.nonce, } ) ) @@ -1596,7 +1594,6 @@ async function renderToStream( clientReferenceManifest.clientModules, { onError: serverComponentsErrorHandler, - nonce: ctx.nonce, } ) @@ -1837,7 +1834,7 @@ async function prerenderToStream( ctx, res.statusCode === 404 ) - function voidOnError() {} + ;( prerenderAsyncStorage.run( // The store to scope @@ -1848,9 +1845,8 @@ async function prerenderToStream( firstAttemptRSCPayload, clientReferenceManifest.clientModules, { - nonce: ctx.nonce, // This render will be thrown away so we don't need to track errors or postpones - onError: voidOnError, + onError: undefined, onPostpone: undefined, // we don't care to track postpones during the prospective render because we need // to always do a final render anyway @@ -1882,13 +1878,13 @@ async function prerenderToStream( } let reactServerIsDynamic = false - function onError(err: unknown, errorInfo: ErrorInfo) { + function onError(err: unknown) { if (err === abortReason || isPrerenderInterruptedError(err)) { reactServerIsDynamic = true return } - return serverComponentsErrorHandler(err, errorInfo) + return serverComponentsErrorHandler(err) } function onPostpone(reason: string) { @@ -1917,7 +1913,6 @@ async function prerenderToStream( finalAttemptRSCPayload, clientReferenceManifest.clientModules, { - nonce: ctx.nonce, onError, onPostpone, signal: flightController.signal, @@ -1946,13 +1941,13 @@ async function prerenderToStream( dynamicTracking, } let SSRIsDynamic = false - function SSROnError(err: unknown, errorInfo: unknown) { + function SSROnError(err: unknown) { if (err === abortReason || isPrerenderInterruptedError(err)) { SSRIsDynamic = true return } - return htmlRendererErrorHandler(err, errorInfo) + return htmlRendererErrorHandler(err) } function SSROnPostpone(reason: string) { @@ -2117,13 +2112,13 @@ async function prerenderToStream( let flightController = new AbortController() let reactServerIsDynamic = false - function onError(err: unknown, errorInfo: ErrorInfo) { + function onError(err: unknown) { if (err === abortReason || isPrerenderInterruptedError(err)) { reactServerIsDynamic = true return } - return serverComponentsErrorHandler(err, errorInfo) + return serverComponentsErrorHandler(err) } dynamicTracking = createDynamicTrackingState( @@ -2157,7 +2152,6 @@ async function prerenderToStream( firstAttemptRSCPayload, clientReferenceManifest.clientModules, { - nonce: ctx.nonce, onError, signal: flightController.signal, } @@ -2224,7 +2218,6 @@ async function prerenderToStream( finalAttemptRSCPayload, clientReferenceManifest.clientModules, { - nonce: ctx.nonce, onError, signal: flightController.signal, } @@ -2262,13 +2255,13 @@ async function prerenderToStream( dynamicTracking, } let SSRIsDynamic = false - function SSROnError(err: unknown, errorInfo: unknown) { + function SSROnError(err: unknown) { if (err === abortReason || isPrerenderInterruptedError(err)) { SSRIsDynamic = true return } - return htmlRendererErrorHandler(err, errorInfo) + return htmlRendererErrorHandler(err) } function SSROnPostpone(_: string) { // We don't really support postponing when PPR is off but since experimental react @@ -2372,7 +2365,6 @@ async function prerenderToStream( clientReferenceManifest.clientModules, { onError: serverComponentsErrorHandler, - nonce: ctx.nonce, } ) )) @@ -2543,7 +2535,6 @@ async function prerenderToStream( clientReferenceManifest.clientModules, { onError: serverComponentsErrorHandler, - nonce: ctx.nonce, } ) )) @@ -2686,7 +2677,6 @@ async function prerenderToStream( clientReferenceManifest.clientModules, { onError: serverComponentsErrorHandler, - nonce: ctx.nonce, } ) diff --git a/packages/next/src/server/app-render/create-error-handler.tsx b/packages/next/src/server/app-render/create-error-handler.tsx index 9db7cbbc49f18..10597355f73cb 100644 --- a/packages/next/src/server/app-render/create-error-handler.tsx +++ b/packages/next/src/server/app-render/create-error-handler.tsx @@ -10,7 +10,7 @@ declare global { var __next_log_error__: undefined | ((err: unknown) => void) } -type ErrorHandler = (err: unknown, errorInfo: unknown) => string | undefined +type ErrorHandler = (err: unknown, errorInfo?: unknown) => string | undefined export type DigestedError = Error & { digest: string } @@ -18,13 +18,18 @@ export function createFlightReactServerErrorHandler( dev: boolean, onReactServerRenderError: (err: any) => void ): ErrorHandler { - return (err: any, errorInfo: any) => { + return (err: any, errorInfo?: unknown) => { // If the error already has a digest, respect the original digest, // so it won't get re-generated into another new error. if (!err.digest) { // TODO-APP: look at using webcrypto instead. Requires a promise to be awaited. err.digest = stringHash( - err.message + (errorInfo?.stack || err.stack || '') + err.message + + (typeof errorInfo === 'object' && + errorInfo !== null && + 'stack' in errorInfo + ? errorInfo.stack + : err.stack || '') ).toString() } diff --git a/packages/next/src/server/stream-utils/node-web-streams-helper.ts b/packages/next/src/server/stream-utils/node-web-streams-helper.ts index dc9beea33b4e8..4e07dd231c6b0 100644 --- a/packages/next/src/server/stream-utils/node-web-streams-helper.ts +++ b/packages/next/src/server/stream-utils/node-web-streams-helper.ts @@ -195,7 +195,7 @@ export function renderToInitialFizzStream({ }: { ReactDOMServer: typeof import('react-dom/server.edge') element: React.ReactElement - streamOptions?: any + streamOptions?: Parameters[1] }): Promise { return getTracer().trace(AppRenderSpan.renderToReadableStream, async () => ReactDOMServer.renderToReadableStream(element, streamOptions) diff --git a/packages/next/types/$$compiled.internal.d.ts b/packages/next/types/$$compiled.internal.d.ts index 6b0860ff32e1c..2848338b28c17 100644 --- a/packages/next/types/$$compiled.internal.d.ts +++ b/packages/next/types/$$compiled.internal.d.ts @@ -27,9 +27,76 @@ declare module 'next/dist/compiled/react-dom/server.edge' declare module 'next/dist/compiled/browserslist' declare module 'react-server-dom-webpack/client' -declare module 'react-server-dom-webpack/server.edge' +declare module 'react-server-dom-webpack/server.edge' { + export function renderToReadableStream( + model: any, + webpackMap: { + readonly [id: string]: { + readonly id: string | number + readonly chunks: readonly string[] + readonly name: string + readonly async?: boolean + } + }, + options?: { + filterStackFrame?: (url: string, functionName: string) => boolean + onError?: (error: unknown) => void + onPostpone?: (reason: string) => void + signal?: AbortSignal + } + ): ReadableStream + + type ServerManifest = {} + + export function decodeReply( + body: string | FormData, + webpackMap: ServerManifest, + options?: { + temporaryReferences?: unknown + } + ): Promise + export function decodeAction( + body: FormData, + serverManifest: ServerManifest + ): Promise<() => T> | null + export function decodeFormState( + actionResult: S, + body: FormData, + serverManifest: ServerManifest + ): Promise + + export function registerServerReference( + reference: T, + id: string, + exportName: string | null + ): unknown + + export function createClientModuleProxy(moduleId: string): unknown +} declare module 'react-server-dom-webpack/server.node' -declare module 'react-server-dom-webpack/static.edge' +declare module 'react-server-dom-webpack/static.edge' { + export function prerender( + children: any, + webpackMap: { + readonly [id: string]: { + readonly id: string | number + readonly chunks: readonly string[] + readonly name: string + readonly async?: boolean + } + }, + options?: { + environmentName?: string | (() => string) + filterStackFrame?: (url: string, functionName: string) => boolean + identifierPrefix?: string + signal?: AbortSignal + onError?: (error: unknown) => void + onPostpone?: (reason: string) => void + } + ): Promise<{ + prelude: ReadableStream + }> +} declare module 'react-server-dom-webpack/client.edge' declare module 'VAR_MODULE_GLOBAL_ERROR' diff --git a/packages/next/types/react-dom.d.ts b/packages/next/types/react-dom.d.ts index 9b811922e4d55..d7151f6d52458 100644 --- a/packages/next/types/react-dom.d.ts +++ b/packages/next/types/react-dom.d.ts @@ -17,7 +17,7 @@ declare module 'react-dom/server.edge' { export type ResumeOptions = { nonce?: string signal?: AbortSignal - onError?: (error: unknown, errorInfo: unknown) => string | undefined + onError?: (error: unknown) => string | undefined onPostpone?: (reason: string) => void unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor } @@ -42,7 +42,7 @@ declare module 'react-dom/server.edge' { bootstrapModules?: Array progressiveChunkSize?: number signal?: AbortSignal - onError?: (error: unknown, errorInfo: unknown) => string | undefined + onError?: (error: unknown) => string | undefined onPostpone?: (reason: string) => void unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor importMap?: { @@ -94,7 +94,7 @@ declare module 'react-dom/static.edge' { bootstrapModules?: Array progressiveChunkSize?: number signal?: AbortSignal - onError?: (error: unknown, errorInfo: unknown) => string | undefined + onError?: (error: unknown) => string | undefined onPostpone?: (reason: string) => void unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor importMap?: {