From 17d8d2db64abaaf68909f99df80cfdad75655044 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 22 Sep 2022 17:38:50 -0700 Subject: [PATCH] Update error handling during app static generation --- .../build/webpack/loaders/next-app-loader.ts | 2 + packages/next/client/components/redirect.ts | 8 ++-- packages/next/server/app-render.tsx | 45 +++++++++++++------ test/e2e/app-dir/app/app/old-router/page.js | 3 ++ 4 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-app-loader.ts b/packages/next/build/webpack/loaders/next-app-loader.ts index 10534259ded03..b3e7012139021 100644 --- a/packages/next/build/webpack/loaders/next-app-loader.ts +++ b/packages/next/build/webpack/loaders/next-app-loader.ts @@ -192,6 +192,8 @@ const nextAppLoader: webpack.LoaderDefinitionFunction<{ : 'null' } + export const staticGenerationAsyncStorage = require('next/dist/client/components/static-generation-async-storage.js').staticGenerationAsyncStorage + export const serverHooks = require('next/dist/client/components/hooks-server-context.js') export const renderToReadableStream = require('next/dist/compiled/react-server-dom-webpack/writer.browser.server').renderToReadableStream diff --git a/packages/next/client/components/redirect.ts b/packages/next/client/components/redirect.ts index 05b30d5a29f6b..eff5cc3f703d0 100644 --- a/packages/next/client/components/redirect.ts +++ b/packages/next/client/components/redirect.ts @@ -17,8 +17,8 @@ export function redirect(url: string) { use(createInfinitePromise()) } // eslint-disable-next-line no-throw-literal - throw { - url, - code: REDIRECT_ERROR_CODE, - } + const error = new Error(REDIRECT_ERROR_CODE) + ;(error as any).url = url + ;(error as any).code = REDIRECT_ERROR_CODE + throw error } diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 661f7d564998b..36a31295e7218 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -75,7 +75,8 @@ function createErrorHandler( /** * Used for debugging */ - _source: string + _source: string, + capturedErrors: Error[] ) { return (err: any) => { if ( @@ -83,23 +84,18 @@ function createErrorHandler( err.message && !err.message.includes('Dynamic server usage') && // TODO-APP: Handle redirect throw - err.code !== REDIRECT_ERROR_CODE + err.code !== REDIRECT_ERROR_CODE && + err.message !== REDIRECT_ERROR_CODE ) { // Used for debugging error source // console.error(_source, err) console.error(err) + capturedErrors.push(err) } - return null } } -const serverComponentsErrorHandler = createErrorHandler( - 'serverComponentsRenderer' -) -const flightDataRendererErrorHandler = createErrorHandler('flightDataRenderer') -const htmlRendererErrorHandler = createErrorHandler('htmlRenderer') - let isFetchPatched = false // we patch fetch to collect cache information used for @@ -111,8 +107,7 @@ function patchFetch(ComponentMod: any) { const { DynamicServerError } = ComponentMod.serverHooks as typeof import('../client/components/hooks-server-context') - const { staticGenerationAsyncStorage } = - require('../client/components/static-generation-async-storage') as typeof import('../client/components/static-generation-async-storage') + const staticGenerationAsyncStorage = ComponentMod.staticGenerationAsyncStorage const origFetch = (global as any).fetch @@ -244,6 +239,7 @@ function createServerComponentRenderer( > rscChunks: Uint8Array[] }, + serverComponentsErrorHandler: ReturnType, nonce?: string ): () => JSX.Element { // We need to expose the `__webpack_require__` API globally for @@ -518,6 +514,21 @@ export async function renderToHTMLOrFlight( isPagesDir: boolean, isStaticGeneration: boolean = false ): Promise { + const capturedErrors: Error[] = [] + + const serverComponentsErrorHandler = createErrorHandler( + 'serverComponentsRenderer', + capturedErrors + ) + const flightDataRendererErrorHandler = createErrorHandler( + 'flightDataRenderer', + capturedErrors + ) + const htmlRendererErrorHandler = createErrorHandler( + 'htmlRenderer', + capturedErrors + ) + const { buildManifest, subresourceIntegrityManifest, @@ -529,8 +540,7 @@ export async function renderToHTMLOrFlight( patchFetch(ComponentMod) - const { staticGenerationAsyncStorage } = - require('../client/components/static-generation-async-storage') as typeof import('../client/components/static-generation-async-storage') + const staticGenerationAsyncStorage = ComponentMod.staticGenerationAsyncStorage if ( !('getStore' in staticGenerationAsyncStorage) && @@ -1145,6 +1155,7 @@ export async function renderToHTMLOrFlight( }, ComponentMod, serverComponentsRenderOpts, + serverComponentsErrorHandler, nonce ) @@ -1248,6 +1259,12 @@ export async function renderToHTMLOrFlight( (await readable.getReader().read()).value || '' ).toString() + // if we encountered any unexpected errors during build + // we fail the prerendering phase and the build + if (capturedErrors.length > 0) { + throw capturedErrors[0] + } + ;(renderOpts as any).pageData = Buffer.concat( serverComponentsRenderOpts.rscChunks ).toString() @@ -1286,7 +1303,7 @@ export async function renderToHTMLOrFlight( return new Promise>>( (resolve, reject) => { staticGenerationAsyncStorage.run(initialStaticGenerationStore, () => { - wrappedRender().then(resolve).catch(reject) + return wrappedRender().then(resolve).catch(reject) }) } ) diff --git a/test/e2e/app-dir/app/app/old-router/page.js b/test/e2e/app-dir/app/app/old-router/page.js index 3a2d5f594fad7..928fa110ca3df 100644 --- a/test/e2e/app-dir/app/app/old-router/page.js +++ b/test/e2e/app-dir/app/app/old-router/page.js @@ -1,6 +1,9 @@ +import { useCookies } from 'next/dist/client/components/hooks-server' import Router from './router' export default function Page() { + useCookies() + return (