diff --git a/packages/next/src/lib/metadata/metadata.tsx b/packages/next/src/lib/metadata/metadata.tsx
index 3ac86f0f74db8..ec5c5f0935162 100644
--- a/packages/next/src/lib/metadata/metadata.tsx
+++ b/packages/next/src/lib/metadata/metadata.tsx
@@ -25,11 +25,13 @@ export async function MetadataTree({
pathname,
searchParams,
getDynamicParamFromSegment,
+ appUsingSizeAdjust,
}: {
tree: LoaderTree
pathname: string
searchParams: { [key: string]: any }
getDynamicParamFromSegment: GetDynamicParamFromSegment
+ appUsingSizeAdjust: boolean
}) {
const metadataContext = {
pathname,
@@ -56,6 +58,8 @@ export async function MetadataTree({
IconsMetadata({ icons: metadata.icons }),
])
+ if (appUsingSizeAdjust) elements.push()
+
return (
<>
{elements.map((el, index) => {
diff --git a/packages/next/src/server/app-render/app-render.tsx b/packages/next/src/server/app-render/app-render.tsx
index 4bd3cf103e025..752eca8ec9e68 100644
--- a/packages/next/src/server/app-render/app-render.tsx
+++ b/packages/next/src/server/app-render/app-render.tsx
@@ -17,7 +17,10 @@ import type { RequestAsyncStorage } from '../../client/components/request-async-
import React from 'react'
import { NotFound as DefaultNotFound } from '../../client/components/error'
-import { createServerComponentRenderer } from './create-server-components-renderer'
+import {
+ createServerComponentRenderer,
+ ErrorHtml,
+} from './create-server-components-renderer'
import { ParsedUrlQuery } from 'querystring'
import { NextParsedUrlQuery } from '../request-meta'
@@ -195,7 +198,7 @@ export async function renderToHTMLOrFlight(
serverActionsBodySizeLimit,
} = renderOpts
- const appUsingSizeAdjust = nextFontManifest?.appUsingSizeAdjust
+ const appUsingSizeAdjust = !!nextFontManifest?.appUsingSizeAdjust
const clientReferenceManifest = renderOpts.clientReferenceManifest!
@@ -1216,8 +1219,8 @@ export async function renderToHTMLOrFlight(
pathname={pathname}
searchParams={providedSearchParams}
getDynamicParamFromSegment={getDynamicParamFromSegment}
+ appUsingSizeAdjust={appUsingSizeAdjust}
/>
- {appUsingSizeAdjust ? : null}
>
),
injectedCSS: new Set(),
@@ -1259,12 +1262,12 @@ export async function renderToHTMLOrFlight(
/** GlobalError can be either the default error boundary or the overwritten app/global-error.js **/
ComponentMod.GlobalError as typeof import('../../client/components/error-boundary').GlobalError
- let serverComponentsInlinedTransformStream: TransformStream<
+ const serverComponentsInlinedTransformStream: TransformStream<
Uint8Array,
Uint8Array
> = new TransformStream()
- let serverErrorComponentsInlinedTransformStream: TransformStream<
+ const serverErrorComponentsInlinedTransformStream: TransformStream<
Uint8Array,
Uint8Array
> = new TransformStream()
@@ -1367,6 +1370,7 @@ export async function renderToHTMLOrFlight(
pathname={pathname}
searchParams={providedSearchParams}
getDynamicParamFromSegment={getDynamicParamFromSegment}
+ appUsingSizeAdjust={appUsingSizeAdjust}
/>
)
@@ -1384,22 +1388,15 @@ export async function renderToHTMLOrFlight(
assetPrefix={assetPrefix}
initialCanonicalUrl={pathname}
initialTree={initialTree}
- initialHead={
- <>
- {createMetadata(loaderTree)}
- {appUsingSizeAdjust ? : null}
- >
- }
+ initialHead={<>{createMetadata(loaderTree)}>}
globalErrorComponent={GlobalError}
notFound={
NotFound ? (
-
-
- {createMetadata(loaderTree)}
- {notFoundStyles}
-
-
-
+
+ {createMetadata(loaderTree)}
+ {notFoundStyles}
+
+
) : undefined
}
asNotFound={props.asNotFound}
@@ -1480,16 +1477,16 @@ export async function renderToHTMLOrFlight(
let polyfillsFlushed = false
let flushedErrorMetaTagsUntilIndex = 0
- const getServerInsertedHTML = () => {
+ const getServerInsertedHTML = (serverCapturedErrors: Error[]) => {
// Loop through all the errors that have been captured but not yet
// flushed.
const errorMetaTags = []
for (
;
- flushedErrorMetaTagsUntilIndex < allCapturedErrors.length;
+ flushedErrorMetaTagsUntilIndex < serverCapturedErrors.length;
flushedErrorMetaTagsUntilIndex++
) {
- const error = allCapturedErrors[flushedErrorMetaTagsUntilIndex]
+ const error = serverCapturedErrors[flushedErrorMetaTagsUntilIndex]
if (isNotFoundError(error)) {
errorMetaTags.push(
@@ -1571,7 +1568,8 @@ export async function renderToHTMLOrFlight(
dataStream: serverComponentsInlinedTransformStream.readable,
generateStaticHTML:
staticGenerationStore.isStaticGeneration || generateStaticHTML,
- getServerInsertedHTML,
+ getServerInsertedHTML: () =>
+ getServerInsertedHTML(allCapturedErrors),
serverInsertedHTMLToHead: true,
...validateRootLayout,
})
@@ -1610,23 +1608,6 @@ export async function renderToHTMLOrFlight(
res.setHeader('Location', getURLFromRedirectError(err))
}
- const defaultErrorComponent = (
-
-
- {/* @ts-expect-error allow to use async server component */}
-
- {appUsingSizeAdjust ? : null}
-
-
-
- )
-
const use404Error = res.statusCode === 404
const useDefaultError = res.statusCode < 400 || res.statusCode === 307
@@ -1643,48 +1624,45 @@ export async function renderToHTMLOrFlight(
? interopDefault(await rootLayoutModule())
: null
- const serverErrorElement = useDefaultError
- ? defaultErrorComponent
- : React.createElement(
- createServerComponentRenderer(
- async () => {
- // only pass plain object to client
- return (
- <>
- {/* @ts-expect-error allow to use async server component */}
-
- {use404Error ? (
+ const serverErrorElement = (
+
+ }
+ >
+ {useDefaultError
+ ? null
+ : React.createElement(
+ createServerComponentRenderer(
+ async () => {
+ return (
<>
-
- {notFoundStyles}
-
-
+ {use404Error ? (
+
+ {notFoundStyles}
+
+
+
+ ) : undefined}
>
- ) : (
-
- )}
- >
+ )
+ },
+ ComponentMod,
+ serverErrorComponentsRenderOpts,
+ serverComponentsErrorHandler,
+ nonce
)
- },
- ComponentMod,
- serverErrorComponentsRenderOpts,
- serverComponentsErrorHandler,
- nonce
- )
- )
+ )}
+
+ )
const renderStream = await renderToInitialStream({
ReactDOMServer: require('react-dom/server.edge'),
@@ -1713,7 +1691,7 @@ export async function renderToHTMLOrFlight(
: serverErrorComponentsInlinedTransformStream
).readable,
generateStaticHTML: staticGenerationStore.isStaticGeneration,
- getServerInsertedHTML,
+ getServerInsertedHTML: () => getServerInsertedHTML([]),
serverInsertedHTMLToHead: true,
...validateRootLayout,
})
diff --git a/packages/next/src/server/app-render/create-server-components-renderer.tsx b/packages/next/src/server/app-render/create-server-components-renderer.tsx
index 0d11231ed110d..5fdc1008bf819 100644
--- a/packages/next/src/server/app-render/create-server-components-renderer.tsx
+++ b/packages/next/src/server/app-render/create-server-components-renderer.tsx
@@ -75,3 +75,18 @@ export function createServerComponentRenderer(
return use(response)
}
}
+
+export function ErrorHtml({
+ head,
+ children,
+}: {
+ head?: React.ReactNode
+ children?: React.ReactNode
+}) {
+ return (
+
+ {head}
+ {children}
+
+ )
+}
diff --git a/test/e2e/app-dir/global-error/app/client/page.js b/test/e2e/app-dir/global-error/basic/app/client/page.js
similarity index 100%
rename from test/e2e/app-dir/global-error/app/client/page.js
rename to test/e2e/app-dir/global-error/basic/app/client/page.js
diff --git a/test/e2e/app-dir/global-error/app/global-error.js b/test/e2e/app-dir/global-error/basic/app/global-error.js
similarity index 100%
rename from test/e2e/app-dir/global-error/app/global-error.js
rename to test/e2e/app-dir/global-error/basic/app/global-error.js
diff --git a/test/e2e/app-dir/global-error/app/layout.js b/test/e2e/app-dir/global-error/basic/app/layout.js
similarity index 100%
rename from test/e2e/app-dir/global-error/app/layout.js
rename to test/e2e/app-dir/global-error/basic/app/layout.js
diff --git a/test/e2e/app-dir/global-error/app/ssr/client/page.js b/test/e2e/app-dir/global-error/basic/app/ssr/client/page.js
similarity index 100%
rename from test/e2e/app-dir/global-error/app/ssr/client/page.js
rename to test/e2e/app-dir/global-error/basic/app/ssr/client/page.js
diff --git a/test/e2e/app-dir/global-error/app/ssr/server/page.js b/test/e2e/app-dir/global-error/basic/app/ssr/server/page.js
similarity index 100%
rename from test/e2e/app-dir/global-error/app/ssr/server/page.js
rename to test/e2e/app-dir/global-error/basic/app/ssr/server/page.js
diff --git a/test/e2e/app-dir/global-error/index.test.ts b/test/e2e/app-dir/global-error/basic/index.test.ts
similarity index 100%
rename from test/e2e/app-dir/global-error/index.test.ts
rename to test/e2e/app-dir/global-error/basic/index.test.ts
diff --git a/test/e2e/app-dir/global-error/layout-error/app/global-error.js b/test/e2e/app-dir/global-error/layout-error/app/global-error.js
new file mode 100644
index 0000000000000..d960556d75cd5
--- /dev/null
+++ b/test/e2e/app-dir/global-error/layout-error/app/global-error.js
@@ -0,0 +1,14 @@
+'use client'
+
+export default function GlobalError({ error }) {
+ return (
+
+
+
+ Global Error
+ {`Global error: ${error?.message}`}
+ {error?.digest && {error?.digest}
}
+
+
+ )
+}
diff --git a/test/e2e/app-dir/global-error/layout-error/app/layout.js b/test/e2e/app-dir/global-error/layout-error/app/layout.js
new file mode 100644
index 0000000000000..6fc4fecd68e10
--- /dev/null
+++ b/test/e2e/app-dir/global-error/layout-error/app/layout.js
@@ -0,0 +1,5 @@
+export default function layout() {
+ throw new Error('Global error: layout error')
+}
+
+export const revalidate = 0
diff --git a/test/e2e/app-dir/global-error/layout-error/app/page.js b/test/e2e/app-dir/global-error/layout-error/app/page.js
new file mode 100644
index 0000000000000..a5192c0ea9cfa
--- /dev/null
+++ b/test/e2e/app-dir/global-error/layout-error/app/page.js
@@ -0,0 +1,3 @@
+export default function page() {
+ return Page
+}
diff --git a/test/e2e/app-dir/global-error/layout-error/index.test.ts b/test/e2e/app-dir/global-error/layout-error/index.test.ts
new file mode 100644
index 0000000000000..707094fdc3176
--- /dev/null
+++ b/test/e2e/app-dir/global-error/layout-error/index.test.ts
@@ -0,0 +1,30 @@
+import { getRedboxHeader, hasRedbox } from 'next-test-utils'
+import { createNextDescribe } from 'e2e-utils'
+
+async function testDev(browser, errorRegex) {
+ expect(await hasRedbox(browser, true)).toBe(true)
+ expect(await getRedboxHeader(browser)).toMatch(errorRegex)
+}
+
+createNextDescribe(
+ 'app dir - global error - layout error',
+ {
+ files: __dirname,
+ skipDeployment: true,
+ },
+ ({ next, isNextDev }) => {
+ it('should render global error for error in server components', async () => {
+ const browser = await next.browser('/')
+
+ if (isNextDev) {
+ await testDev(browser, /Global error: layout error/)
+ } else {
+ expect(await browser.elementByCss('h1').text()).toBe('Global Error')
+ expect(await browser.elementByCss('#error').text()).toBe(
+ 'Global error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.'
+ )
+ expect(await browser.elementByCss('#digest').text()).toMatch(/\w+/)
+ }
+ })
+ }
+)